다음을 통해 공유


Linux에서 첫 번째 Service Fabric 컨테이너 애플리케이션 만들기

Service Fabric 클러스터의 Linux 컨테이너에서 기존 애플리케이션을 실행해도 애플리케이션을 변경할 필요가 없습니다. 이 문서에서는 Python Flask 웹 애플리케이션을 포함하는 Docker 이미지를 만들고 Service Fabric 클러스터에 배포하는 방법을 안내합니다. 또한 Azure Container Registry를 통해 컨테이너화된 애플리케이션을 공유합니다. 이 문서에서는 Docker에 대한 기본적인 이해를 가정합니다. Docker 개요를 참조하여 Docker에 대해 알아볼 수 있습니다.

메모

이 문서는 Linux 개발 환경에 적용됩니다. Service Fabric 클러스터 런타임 및 Docker 런타임은 동일한 OS에서 실행되어야 합니다. Windows 클러스터에서는 Linux 컨테이너를 실행할 수 없습니다.

사전 요구 사항

Docker 컨테이너 정의

Docker 허브에 있는 Python 이미지를 기반으로 이미지를 빌드합니다.

Dockerfile에서 Docker 컨테이너를 지정합니다. Dockerfile은 컨테이너 내에서 환경을 설정하고, 실행할 애플리케이션을 로드하고, 포트를 매핑하기 위한 지침으로 구성됩니다. Dockerfile은 이미지를 만드는 명령에 대한 docker build 입력입니다.

빈 디렉터리를 만들고 파일 확장명 없이 Dockerfile 파일을 만듭니다. Dockerfile에 다음을 추가하고 변경 내용을 저장합니다.

# Use an official Python runtime as a base image
FROM python:2.7-slim

# Set the working directory to /app
WORKDIR /app

# Copy the current directory contents into the container at /app
ADD . /app

# Install any needed packages specified in requirements.txt
RUN pip install -r requirements.txt

# Make port 80 available outside this container
EXPOSE 80

# Define environment variable
ENV NAME World

# Run app.py when the container launches
CMD ["python", "app.py"]

자세한 내용은 Dockerfile 참조 를 참조하세요.

기본 웹 애플리케이션 만들기

"Hello World!"를 반환하는 포트 80에서 수신 대기하는 Flask 웹 애플리케이션을 만듭니다. 동일한 디렉터리에서 파일 requirements.txt만듭니다. 다음을 추가하고 변경 내용을 저장합니다.

Flask

또한 app.py 파일을 만들고 다음 코드 조각을 추가합니다.

from flask import Flask

app = Flask(__name__)


@app.route("/")
def hello():

    return 'Hello World!'


if __name__ == "__main__":
    app.run(host='0.0.0.0', port=80)

Docker에 로그인하고 이미지 빌드

다음으로 웹 애플리케이션을 실행하는 이미지를 만듭니다. Docker에서 공용 이미지를 끌어올 때(예: python:2.7-slim Dockerfile에서) 익명 끌어오기 요청을 만드는 대신 Docker Hub 계정으로 인증하는 것이 가장 좋습니다.

메모

익명 끌어오기 요청을 자주 수행할 때 ERROR: toomanyrequests: Too Many Requests. 또는 You have reached your pull rate limit.와 유사한 Docker 오류가 나타날 수 있습니다. 이러한 오류를 방지하려면 Docker Hub에 인증하세요. 자세한 내용은 Azure Container Registry를 사용하여 공용 콘텐츠 관리를 참조하세요.

PowerShell 창을 열고 Dockerfile이 포함된 디렉터리로 이동합니다. 그런 다음, 다음 명령을 실행합니다.

docker login
docker build -t helloworldapp .

이 명령은 Dockerfile의 지침을 사용하여 새 이미지를 빌드하고 이미지의 이름을 지정(-t 태그 지정) helloworldapp합니다. 컨테이너 이미지를 빌드하기 위해 기본 이미지는 먼저 애플리케이션이 추가되는 Docker Hub에서 다운로드됩니다.

빌드 명령이 완료되면 명령을 실행 docker images 하여 새 이미지에 대한 정보를 확인합니다.

$ docker images
    
REPOSITORY                    TAG                 IMAGE ID            CREATED             SIZE
helloworldapp                 latest              86838648aab6        2 minutes ago       194 MB

애플리케이션을 로컬로 실행

컨테이너화된 애플리케이션이 컨테이너 레지스트리를 푸시하기 전에 로컬로 실행되는지 확인합니다.

애플리케이션을 실행하여 컴퓨터의 포트 4000을 컨테이너의 노출된 포트 80에 매핑합니다.

docker run -d -p 4000:80 --name my-web-site helloworldapp

이름은 컨테이너 ID 대신 실행 중인 컨테이너에 이름을 지정합니다.

실행 중인 컨테이너에 연결합니다. 포트 4000에서 반환된 IP 주소를 가리키는 웹 브라우저를 엽니다(예: "http://localhost:4000"). 브라우저에 "Hello World!" 라는 제목이 표시됩니다.

전 세계 여러분 안녕하세요!

컨테이너를 중지하려면 다음을 실행합니다.

docker stop my-web-site

개발 컴퓨터에서 컨테이너를 삭제합니다.

docker rm my-web-site

컨테이너 레지스트리에 이미지 푸시

애플리케이션이 Docker에서 실행되는지 확인한 후 Azure Container Registry의 레지스트리에 이미지를 푸시합니다.

레지스트리 docker login을 사용하여 컨테이너 레지스트리에 로그인하려면 실행 합니다.

다음 예제에서는 Microsoft Entra 서비스 주체의 ID와 암호를 전달합니다. 예를 들어 자동화 시나리오를 위해 레지스트리에 서비스 주체를 할당했을 수 있습니다. 또는 레지스트리 사용자 이름 및 암호를 사용하여 로그인할 수 있습니다.

docker login myregistry.azurecr.io -u xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx -p myPassword

다음 명령은 레지스트리에 대한 정규화된 경로를 사용하여 이미지의 태그 또는 별칭을 만듭니다. 다음은 레지스트리의 루트에서 samples 혼란을 방지하기 위해 네임스페이스에 이미지를 배치하는 예제입니다.

docker tag helloworldapp myregistry.azurecr.io/samples/helloworldapp

이미지를 컨테이너 레지스트리에 푸시합니다.

docker push myregistry.azurecr.io/samples/helloworldapp

Yeoman을 사용하여 Docker 이미지 패키지

Linux용 Service Fabric SDK에는 애플리케이션을 쉽게 만들고 컨테이너 이미지를 추가할 수 있는 Yeoman 생성기가 포함되어 있습니다. Yeoman을 사용하여 SimpleContainerApp이라는 단일 Docker 컨테이너를 사용하여 애플리케이션을 만들어 보겠습니다.

Service Fabric 컨테이너 애플리케이션을 만들려면 터미널 창을 열고 실행 yo azuresfcontainer합니다.

애플리케이션 이름(예: mycontainer)을 지정하고 애플리케이션 서비스 이름을 지정합니다(예: myservice).

이미지 이름의 경우 컨테이너 레지스트리에서 컨테이너 이미지의 URL(예: "myregistry.azurecr.io/samples/helloworldapp")을 제공합니다.

이 이미지에는 워크로드 진입점이 정의되어 있으므로 입력 명령을 명시적으로 지정할 필요가 없습니다(명령은 컨테이너 내에서 실행되며 시작 후에도 컨테이너가 계속 실행됨).

인스턴스 수를 "1"로 지정합니다.

적절한 형식으로 포트 매핑을 지정합니다. 이 문서에서는 80:4000을(를) 포트 매핑으로 설정해야 합니다. 이렇게 하면 호스트 컴퓨터의 포트 4000에 들어오는 모든 요청이 컨테이너의 포트 80으로 리디렉션되도록 구성했습니다.

컨테이너용 Service Fabric Yeoman 생성기

컨테이너 리포지토리 인증 구성

컨테이너 이미지 다운로드를 위해 다양한 유형의 인증을 구성하는 방법을 알아보려면 컨테이너 리포지토리인증을 참조하세요.

격리 모드 구성

6.3 런타임 릴리스에서는 Linux 컨테이너에 대해 VM 격리가 지원되므로 컨테이너에 대한 두 가지 격리 모드인 프로세스 및 Hyper-V를 지원합니다. Hyper-V 격리 모드에서는 커널이 각 컨테이너와 컨테이너 호스트 간에 격리됩니다. Hyper-V 격리는 Clear Containers를 사용하여 구현됩니다. 격리 모드는 애플리케이션 매니페스트 파일의 요소에 ServicePackageContainerPolicy 있는 Linux 클러스터에 대해 지정됩니다. 격리 모드를 지정할 수 있는 경우는 process, hypervdefault입니다. 기본값은 프로세스 격리 모드입니다. 다음 코드 조각은 애플리케이션 매니페스트 파일에서 격리 모드를 지정하는 방법을 보여줍니다.

<ServiceManifestImport>
    <ServiceManifestRef ServiceManifestName="MyServicePkg" ServiceManifestVersion="1.0.0"/>
      <Policies>
        <ServicePackageContainerPolicy Hostname="votefront" Isolation="hyperv">
          <PortBinding ContainerPort="80" EndpointRef="myServiceTypeEndpoint"/>
        </ServicePackageContainerPolicy>
    </Policies>
  </ServiceManifestImport>

리소스 거버넌스 구성

리소스 거버넌스 는 컨테이너가 호스트에서 사용할 수 있는 리소스를 제한합니다. ResourceGovernancePolicy 애플리케이션 매니페스트에 지정된 요소는 서비스 코드 패키지에 대한 리소스 제한을 선언하는 데 사용됩니다. 리소스 제한은 메모리, MemorySwap, CpuShares(CPU 상대 가중치), MemoryReservationInMB, BlkioWeight(BlockIO 상대 가중치) 리소스에 대해 설정할 수 있습니다. 이 예제에서 서비스 패키지 Guest1Pkg는 배치된 클러스터 노드에서 하나의 코어를 가져옵니다. 메모리 제한은 절대적이므로 코드 패키지는 1024MB의 메모리(및 동일한 소프트 보장 예약)로 제한됩니다. 코드 패키지(컨테이너 또는 프로세스)는 이 제한보다 더 많은 메모리를 할당할 수 없으며, 이를 시도하면 메모리 부족 예외가 발생합니다. 리소스 제한 적용이 작동하려면 서비스 패키지 내의 모든 코드 패키지에 지정된 메모리 제한이 있어야 합니다.

<ServiceManifestImport>
  <ServiceManifestRef ServiceManifestName="MyServicePKg" ServiceManifestVersion="1.0.0" />
  <Policies>
    <ServicePackageResourceGovernancePolicy CpuCores="1"/>
    <ResourceGovernancePolicy CodePackageRef="Code" MemoryInMB="1024"  />
  </Policies>
</ServiceManifestImport>

도커 HEALTHCHECK 구성

v6.1부터 Service Fabric은 Docker HEALTHCHECK 이벤트를 시스템 상태 보고서에 자동으로 통합합니다. 즉, 컨테이너 에 HEALTHCHECK 를 사용하도록 설정된 경우 Docker에서 보고한 대로 컨테이너의 상태가 변경될 때마다 Service Fabric에서 상태를 보고합니다. health_status 정상이면 Service Fabric Explorer정상 상태 보고서가 표시되고 health_status비정상일 때경고가 표시됩니다.

v6.4의 최신 새로 고침 릴리스부터 DOCKER HEALTHCHECK 평가를 오류로 보고하도록 지정하는 옵션이 있습니다. 이 옵션을 사용하도록 설정하면 상태_건강정상일 때 OK 상태 보고서가 표시되고, 상태_건강비정상일 때 ERROR가 표시됩니다.

컨테이너 상태를 모니터링하기 위해 수행되는 실제 검사를 가리키는 HEALTHCHECK 명령은 컨테이너 이미지를 생성하는 동안 사용되는 Dockerfile에 있어야 합니다.

스크린샷은 배포된 서비스 패키지 NodeServicePackage의 세부 정보를 보여줍니다.

HealthCheckUnhealthyApp

HealthCheckUnhealthyDsp

ApplicationManifest에서 ContainerHostPolicies의 일부로 HealthConfig 옵션을 지정하여 각 컨테이너에 대한 HEALTHCHECK 동작을 구성할 수 있습니다.

<ServiceManifestImport>
    <ServiceManifestRef ServiceManifestName="ContainerServicePkg" ServiceManifestVersion="2.0.0" />
    <Policies>
      <ContainerHostPolicies CodePackageRef="Code">
        <HealthConfig IncludeDockerHealthStatusInSystemHealthReport="true"
		      RestartContainerOnUnhealthyDockerHealthStatus="false" 
		      TreatContainerUnhealthyStatusAsError="false" />
      </ContainerHostPolicies>
    </Policies>
</ServiceManifestImport>

기본적으로 IncludeDockerHealthStatusInSystemHealthReporttrue로 설정되고 RestartContainerOnUnhealthyDockerHealthStatusfalse로 설정되고 TreatContainerUnhealthyStatusAsErrorfalse로 설정됩니다.

RestartContainerOnUnhealthyDockerHealthStatustrue로 설정되면 비정상 상태임을 반복적으로 보고하는 컨테이너가 다시 시작됩니다(다른 노드에서).

TreatContainerUnhealthyStatusAsErrortrue로 설정되면 컨테이너의 health_status비정상일 때 ERROR 상태 보고서가 표시됩니다.

전체 Service Fabric 클러스터에 HEALTHCHECK 통합을 사용하지 않도록 설정하려면 EnableDockerHealthCheckIntegrationfalse로 설정해야 합니다.

애플리케이션 배포

애플리케이션이 빌드되면 Service Fabric CLI를 사용하여 로컬 클러스터에 배포할 수 있습니다.

로컬 Service Fabric 클러스터에 연결합니다.

sfctl cluster select --endpoint http://localhost:19080

템플릿 https://github.com/Azure-Samples/service-fabric-containers/ 에 제공된 설치 스크립트를 사용하여 애플리케이션 패키지를 클러스터의 이미지 저장소에 복사하고, 애플리케이션 유형을 등록하고, 애플리케이션 인스턴스를 만듭니다.

./install.sh

브라우저를 열고 Service Fabric Explorer로 이동합니다(Mac OS X에서 http://localhost:19080/Explorer Vagrant를 사용하는 경우 localhost를 VM의 개인 IP로 바꿉니다). 애플리케이션 노드를 확장하고, 애플리케이션 유형에 대한 항목과 해당 유형의 첫 번째 인스턴스에 대한 항목이 이제 생성되었음을 확인하세요.

실행 중인 컨테이너에 연결합니다. 포트 4000에서 반환된 IP 주소를 가리키는 웹 브라우저를 엽니다(예: "http://localhost:4000"). 브라우저에 "Hello World!" 라는 제목이 표시됩니다.

전 세계 여러분 안녕하세요!

정리

템플릿에 제공된 제거 스크립트를 사용하여 로컬 개발 클러스터에서 애플리케이션 인스턴스를 삭제하고 애플리케이션 유형을 등록 취소합니다.

./uninstall.sh

이미지를 컨테이너 레지스트리에 푸시한 후 개발 컴퓨터에서 로컬 이미지를 삭제할 수 있습니다.

docker rmi helloworldapp
docker rmi myregistry.azurecr.io/samples/helloworldapp

전체 예제 Service Fabric 애플리케이션 및 서비스 매니페스트

다음은 이 문서에서 사용되는 전체 서비스 및 애플리케이션 매니페스트입니다.

ServiceManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<ServiceManifest Name="myservicePkg"
                 Version="1.0.0"
                 xmlns="http://schemas.microsoft.com/2011/01/fabric"
                 xmlns:xsd="https://www.w3.org/2001/XMLSchema"
                 xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance">
  <ServiceTypes>
    <!-- This is the name of your ServiceType.
         The UseImplicitHost attribute indicates this is a guest service. -->
    <StatelessServiceType ServiceTypeName="myserviceType" UseImplicitHost="true" />
  </ServiceTypes>

  <!-- Code package is your service executable. -->
  <CodePackage Name="Code" Version="1.0.0">
    <EntryPoint>
      <!-- Follow this link for more information about deploying containers 
      to Service Fabric: https://aka.ms/sfguestcontainers -->
      <ContainerHost>
        <ImageName>myregistry.azurecr.io/samples/helloworldapp</ImageName>
        <!-- Pass comma delimited commands to your container: dotnet, myproc.dll, 5" -->
        <!--Commands> dotnet, myproc.dll, 5 </Commands-->
        <Commands></Commands>
      </ContainerHost>
    </EntryPoint>
    <!-- Pass environment variables to your container: -->
    
    <EnvironmentVariables>
      <!--
      <EnvironmentVariable Name="VariableName" Value="VariableValue"/>
      -->
    </EnvironmentVariables>
    
  </CodePackage>

  <Resources>
    <Endpoints>
      <!-- This endpoint is used by the communication listener to obtain the port on which to 
           listen. Please note that if your service is partitioned, this port is shared with 
           replicas of different partitions that are placed in your code. -->
      <Endpoint Name="myServiceTypeEndpoint" UriScheme="http" Port="4000" Protocol="http"/>
    </Endpoints>
  </Resources>
</ServiceManifest>

ApplicationManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<ApplicationManifest ApplicationTypeName="mycontainerType"
                     ApplicationTypeVersion="1.0.0"
                     xmlns="http://schemas.microsoft.com/2011/01/fabric"
                     xmlns:xsd="https://www.w3.org/2001/XMLSchema"
                     xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance">
  <!-- Import the ServiceManifest from the ServicePackage. The ServiceManifestName and ServiceManifestVersion 
       should match the Name and Version attributes of the ServiceManifest element defined in the 
       ServiceManifest.xml file. -->
  <ServiceManifestImport>
    <ServiceManifestRef ServiceManifestName="myservicePkg" ServiceManifestVersion="1.0.0" />
    <ConfigOverrides />
    <Policies>
      <ContainerHostPolicies CodePackageRef="Code">
        <RepositoryCredentials AccountName="myregistry" Password="=P==/==/=8=/=+u4lyOB=+=nWzEeRfF=" PasswordEncrypted="false"/>
        <PortBinding ContainerPort="80" EndpointRef="myServiceTypeEndpoint"/>
      </ContainerHostPolicies>
    </Policies>
  </ServiceManifestImport>
  <DefaultServices>
    <!-- The section below creates instances of service types, when an instance of this 
         application type is created. You can also create one or more instances of service type using the 
         ServiceFabric PowerShell module.
         
         The attribute ServiceTypeName below must match the name defined in the imported ServiceManifest.xml file. -->
    <Service Name="myservice">
      <!-- On a local development cluster, set InstanceCount to 1. On a multi-node production 
      cluster, set InstanceCount to -1 for the container service to run on every node in 
      the cluster.
      -->
      <StatelessService ServiceTypeName="myserviceType" InstanceCount="1">
        <SingletonPartition />
      </StatelessService>
    </Service>
  </DefaultServices>
</ApplicationManifest>

기존 애플리케이션에 더 많은 서비스 추가

yeoman을 사용하여 이미 만든 애플리케이션에 다른 컨테이너 서비스를 추가하려면 다음 단계를 수행합니다.

  1. 디렉터리를 기존 애플리케이션의 루트로 변경합니다. 예를 들어 cd ~/YeomanSamples/MyApplicationYeoman에서 만든 애플리케이션인 경우 MyApplication 입니다.
  2. yo azuresfcontainer:AddService을 실행합니다.

컨테이너가 강제로 종료되기 전에 시간 간격 구성

서비스 삭제(또는 다른 노드로의 이동)가 시작된 후 컨테이너가 제거되기 전에 런타임이 대기하도록 시간 간격을 구성할 수 있습니다. 시간 간격을 구성하면 명령이 docker stop <time in seconds> 컨테이너로 전송됩니다. 자세한 내용은 docker 중지를 참조하세요. 대기할 시간 간격은 섹션 아래에 Hosting 지정됩니다. 다음 클러스터 매니페스트 코드 조각은 대기 간격을 설정하는 방법을 보여줍니다.

{
        "name": "Hosting",
        "parameters": [
          {
                "name": "ContainerDeactivationTimeout",
                "value" : "10"
          },
	      ...
        ]
}

기본 시간 간격은 10초로 설정됩니다. 이 구성은 동적이므로 클러스터의 구성만 업그레이드하면 시간 제한이 업데이트됩니다.

사용하지 않는 컨테이너 이미지를 제거하도록 런타임 구성

노드에서 사용되지 않는 컨테이너 이미지를 제거하도록 Service Fabric 클러스터를 구성할 수 있습니다. 이 구성을 사용하면 노드에 컨테이너 이미지가 너무 많은 경우 디스크 공간을 다시 캡처할 수 있습니다. 이 기능을 사용하도록 설정하려면 클러스터 매니페스트의 Hosting 섹션을 다음 예시와 같이 업데이트합니다.

{
        "name": "Hosting",
        "parameters": [
          {
                "name": "PruneContainerImages",
                "value": "True"
          },
          {
                "name": "ContainerImagesToSkip",
                "value": "mcr.microsoft.com/windows/servercore|mcr.microsoft.com/windows/nanoserver|mcr.microsoft.com/dotnet/framework/aspnet|..."
          }
          ...
          }
        ]
} 

삭제해서는 안 되는 이미지의 경우 매개 변수 아래에 ContainerImagesToSkip 지정할 수 있습니다.

컨테이너 이미지 다운로드 시간 구성

Service Fabric 런타임은 대부분의 컨테이너 이미지에서 작동하는 컨테이너 이미지를 다운로드하고 추출하는 데 20분을 할당합니다. 큰 이미지의 경우 또는 네트워크 연결 속도가 느린 경우 이미지 다운로드 및 추출을 중단하기 전에 대기 시간을 늘려야 할 수 있습니다. 이 시간 제한은 다음 코드 조각과 같이 클러스터 매니페스트의 호스팅 섹션에서 ContainerImageDownloadTimeout 특성을 사용하여 설정됩니다.

{
        "name": "Hosting",
        "parameters": [
          {
              "name": "ContainerImageDownloadTimeout",
              "value": "1200"
          }
        ]
}

컨테이너 보존 정책 설정

컨테이너 시작 오류 진단을 지원하기 위해 Service Fabric(버전 6.1 이상)은 종료되거나 시작하지 못한 컨테이너를 유지하도록 지원합니다. 이 정책은 다음 코드 조각과 같이 ApplicationManifest.xml 파일에서 설정할 수 있습니다.

 <ContainerHostPolicies CodePackageRef="NodeService.Code" Isolation="process" ContainersRetentionCount="2"  RunInteractive="true"> 

ContainersRetentionCount 설정은 실패할 때 유지할 컨테이너 수를 지정합니다. 음수 값을 지정하면 실패한 모든 컨테이너가 유지됩니다. ContainersRetentionCount 특성을 지정하지 않으면 컨테이너가 유지되지 않습니다. 또한 ContainersRetentionCount 특성은 사용자가 테스트 및 프로덕션 클러스터에 대해 다른 값을 지정할 수 있도록 애플리케이션 매개 변수를 지원합니다. 배치 제약 조건을 사용하여 컨테이너 서비스가 다른 노드로 이동하지 못하도록 이 기능을 사용할 때 컨테이너 서비스를 특정 노드로 대상으로 지정합니다. 이 기능을 사용하여 유지되는 모든 컨테이너는 수동으로 제거해야 합니다.

사용자 지정 인수를 사용하여 Docker 디먼 시작

6.2 버전의 Service Fabric 런타임 이상에서는 사용자 지정 인수를 사용하여 Docker 디먼을 시작할 수 있습니다. 사용자 지정 인수를 지정하면 Service Fabric은 지정된 --pidfile 인수만 docker 엔진에 전달하고 다른 어떤 인수도 전달하지 않습니다. --pidfile 따라서 인수로 전달해서는 안 됩니다. 또한 이 인수는 Service Fabric이 디먼과 통신할 수 있도록 Windows의 기본 이름 파이프(또는 Linux의 unix 도메인 소켓)에서 docker 디먼이 계속 수신 대기하도록 해야 합니다. 사용자 지정 인수는 ContainerServiceArguments 아래의 호스팅 섹션 아래 클러스터 매니페스트에 지정됩니다. 예제는 다음 코드 조각에 나와 있습니다.

{ 
        "name": "Hosting", 
        "parameters": [ 
          { 
            "name": "ContainerServiceArguments", 
            "value": "-H localhost:1234 -H unix:///var/run/docker.sock" 
          } 
        ] 
} 

다음 단계