콘텐츠로 이동

Greengrass Component

greengrass#component 생성기는 기존 Python 또는 TypeScript 프로젝트에 AWS IoT Greengrass v2 컴포넌트를 추가합니다. 레시피, 진입점, Python 컴포넌트용 단위 테스트, 그리고 코어 디바이스용 컴포넌트를 패키징하는 Nx 타겟을 생성합니다. 기본 infra를 사용하면 컴포넌트 버전을 게시하고, 아티팩트 리포지토리를 소유하며, 배포를 설명하는 재사용 가능한 CDK 구성 요소 또는 Terraform 모듈도 제공합니다. 이러한 빌딩 블록을 자체 인프라 코드에서 구성할 수 있으며, 생성기는 디바이스, 플릿 또는 배포를 직접 생성하지 않습니다.

호스트 프로젝트가 이미 존재해야 합니다. 먼저 py#project 또는 ts#project로 프로젝트를 생성하세요.

이 제너레이터 실행@aws/nx-plugin:greengrass#component

pnpm nx g @aws/nx-plugin:greengrass#component --project=telemetry --name=pulse --language=python
명령 구성하기12

필수

필수

스크립트와 문서에서는 language를 명시적으로 전달하세요. 생략하면 생성기는 선택한 프로젝트의 tsconfig.json에서 typescript를 추론하거나, pyproject.toml에서 python을 추론합니다. 두 파일이 모두 있는 프로젝트는 --language를 전달해야 하며, 두 파일이 모두 없는 프로젝트는 지원되는 호스트가 아닙니다.

ipc는 두 언어 모두에서 기본적으로 false입니다. Greengrass IPC 액세스를 추가하려면 --ipc=true를 전달하고, 비활성화 상태를 유지하려면 --ipc=false를 전달하세요.

레시피의 ComponentPublisher는 기본적으로 워크스페이스 npm 스코프로 설정됩니다. 다른 값을 설정하려면 --publisher를 전달하세요. aws-nx-plugin.config.mts에는 Greengrass 게시자 설정이 없습니다.

iac: terraform의 경우, 이 생성기를 실행하기 전에 terraform#project로 Terraform 애플리케이션을 생성하세요. Greengrass 생성기는 해당 애플리케이션에 awscc 프로바이더 구성을 추가합니다. Terraform 애플리케이션을 나중에 생성하는 경우, 프로바이더를 추가할 수 있도록 greengrass#component를 다시 실행하세요.

--project는 정규화되지 않은 프로젝트 이름을 허용합니다. 생성된 프로젝트는 네임스페이스로 정규화되므로 nx run에는 전체 이름이 필요합니다: Python 프로젝트의 경우 <scope>.telemetry, TypeScript 프로젝트의 경우 @<scope>/telemetry. 아래 예제에서는 해당 이름을 <project>로 표기하고, npm 스코프가 my-scope인 워크스페이스에서 --name=infra로 생성된 인프라 프로젝트를 @my-scope/infra로 표기합니다.

platform은 보드 이름이 아닌 코어 디바이스의 운영 체제와 CPU 아키텍처를 선택합니다. 현재 타겟은 glibc를 사용하는 64비트 Linux입니다:

디바이스생성기 타겟상태
Linux PC, 산업용 게이트웨이 또는 EC2 x86_64 인스턴스linux-amd64지원됨; EC2 코어에서 검증됨
64비트 CPU와 64비트 Linux를 사용하는 Raspberry Pi 또는 다른 Linux ARM64 게이트웨이linux-arm64패키징 지원됨; Raspberry Pi 하드웨어는 검증되지 않음
위 두 가지를 모두 포함하는 플릿linux-amd64-arm64아키텍처별 매니페스트가 있는 하나의 컴포넌트 버전
32비트 Linux / ARMv7을 실행하는 Raspberry Pi없음이 생성기에서 지원되지 않음, 클래식 Greengrass nucleus는 ARMv7을 지원함
Raspberry Pi Pico, ESP32 또는 다른 마이크로컨트롤러없음코어 디바이스의 IoT 클라이언트 또는 주변 장치로 사용

Raspberry Pi에서는 uname -m과 getconf LONG_BIT를 모두 확인하세요. 이 경로에는 aarch64와 64가 필요합니다. 32비트 사용자 공간을 실행하는 64비트 프로세서는 ARM64 배포 타겟이 아닙니다. 디바이스에는 생성된 레시피에 명시된 Python 인터프리터 또는 TypeScript용 Node.js와 Greengrass nucleus 전제 조건이 필요합니다. Python 종속성 벤더링은 manylinux_2_28을 타겟으로 하므로 glibc 2.28 이상을 사용하세요. GPIO 라이브러리와 사용자 정의 네이티브 종속성은 선택한 플랫폼에 호환되는 바이너리를 제공해야 합니다. 생성기는 이러한 주변 장치를 프로비저닝하지 않습니다.

생성된 Python 레시피는 호스트 프로젝트의 requires-python 최소값(예: python3.14)에 인터프리터를 고정합니다. 컴포넌트를 실행하는 각 코어 디바이스에 해당 인터프리터를 설치하세요. TypeScript 컴포넌트는 코어 디바이스에 Node.js가 필요합니다.

linux-amd64-arm64의 경우, 레시피에는 아키텍처당 하나의 매니페스트가 포함됩니다. Greengrass는 Platform이 코어 디바이스와 일치하는 첫 번째 매니페스트를 선택하므로, 각 매니페스트의 아티팩트 및 환경 설정을 해당 아키텍처와 쌍으로 유지하세요.

ESP32 펌웨어는 이러한 Python 및 TypeScript 컴포넌트와 별도로 실행됩니다. AWS IoT Core에 MQTT 메시지를 보내거나 클라이언트 디바이스 지원을 사용하여 로컬 Greengrass 코어에 연결할 수 있습니다. 로컬 경로에는 브로커, 클라이언트 인증 및 MQTT 브리지 구성이 필요하며, 이 생성기는 이들이나 펌웨어를 스캐폴딩하지 않습니다. ARMv7, Windows, musl Linux 및 기타 CPU 아키텍처에는 추가 패키징 및 런타임 작업이 필요합니다. 레시피의 아키텍처 레이블만 변경하는 것으로는 충분하지 않습니다.

제너레이터 옵션12 옵션
project필수string

기존 Python 또는 TypeScript 호스트 프로젝트

name필수string

컴포넌트 이름

platformenum기본값: linux-arm64

코어 디바이스 플랫폼 또는 플랫폼들.

linux-amd64linux-arm64linux-amd64-arm64
infraenum기본값: component-version

component-version은 재사용 가능한 Greengrass 컴포넌트 인프라를 제공하며, none은 로컬 개발만 지원합니다.

component-versionnone
iacenum기본값: inherit

IaC 프로바이더, 인프라가 요청될 때만 해석됩니다.

inheritcdkterraform
languageenum

컴포넌트 언어. 호스트에 지원되는 언어 마커가 정확히 하나만 있을 때만 생략합니다.

typescriptpython
componentNamestring

레시피 ComponentName. 워크스페이스 npm 스코프와 컴포넌트 이름에서 파생된 역방향 도메인 이름이 기본값입니다.

componentVersionstring기본값: 1.0.0

레시피 ComponentVersion. CDK는 배포 시 NEXT_*를 해석하며, Terraform은 명시적인 시맨틱 버전이 필요합니다. infra=none인 경우 NEXT_*가 변경되지 않고 그대로 작성됩니다.

publisherstring

레시피 ComponentPublisher. 워크스페이스 npm 스코프가 기본값입니다.

ipcboolean기본값: false

Greengrass IPC 클라이언트를 연결할지 여부. 두 언어 모두 기본값은 false입니다.

gdkConfigboolean기본값: false

GDK CLI를 수동으로 사용하기 위한 gdk-config.json을 제공합니다. GDK는 NEXT_PATCH를 지원하며, NEXT_MINOR 또는 NEXT_MAJOR는 지원하지 않습니다.

preferInstallDependenciesboolean기본값: true

제너레이터 실행 후 의존성 설치를 선호할지 여부.

language = python
  • 디렉터리greengrass
    • 디렉터리pulse
      • recipe.yaml 컴포넌트 레시피
      • main.py 컴포넌트 진입점
  • 디렉터리tests
    • 디렉터리greengrass
      • test_pulse.py 경로로 main.py를 로드하는 단위 테스트

pulse-vendor 타겟은 uv pip install --only-binary :all:을 사용하여 선택한 디바이스 플랫폼용 프로젝트 종속성을 벤더링합니다. platform: linux-amd64-arm64의 경우, 아키텍처당 한 번씩 벤더링하고 레시피는 아키텍처당 하나의 매니페스트를 포함합니다.

language = typescript
  • 디렉터리greengrass
    • 디렉터리pulse
      • recipe.yaml 컴포넌트 레시피
  • 디렉터리src
    • 디렉터리greengrass
      • 디렉터리pulse
        • main.ts 컴포넌트 진입점

프로젝트의 bundle 타겟은 패키징 전에 진입점을 번들링합니다. ipc: true의 경우, pulse-vendor 타겟은 선택한 아키텍처용 aws-crt 네이티브 애드온도 스테이징합니다.

레시피, 진입점, 그리고 Python의 경우 테스트는 첫 실행부터 사용자의 것입니다: 재실행해도 다시 작성되지 않습니다. 타겟, 메타데이터 및 종속성 선언은 프레임워크에서 관리하며 매 실행마다 수렴됩니다. packages/common/scripts/src/greengrass/ 아래의 공유 패키징 스크립트는 한 번 생성됩니다. 해당 디렉토리를 삭제하고 생성기를 다시 실행하여 새로 고칠 수 있습니다.

타겟은 케밥 케이스 컴포넌트 이름을 사용합니다:

타겟목적
<component>-vendorPython 종속성 또는 IPC가 활성화된 경우 TypeScript CRT 애드온을 벤더링
<component>-artifactdist/<project-root>/greengrass/<component>/greengrass-build/ 아래에 해결된 레시피와 배포 가능한 아카이브를 작성
<component>-deploy-local코어 디바이스에서 greengrass-cli를 통해 로컬 배포를 생성하고 안정된 컴포넌트 상태를 기다림
<component>-logs코어 디바이스에서 컴포넌트 로그를 테일링

<component>-artifact는 호스트 프로젝트의 build 및 assemble 타겟의 종속성이므로, 일반 빌드는 아티팩트를 생성합니다.

infra = component-version

이 생성기는 선택한 iac를 기반으로 코드형 인프라를 제공하므로, 관련 CDK constructs 또는 Terraform 모듈을 포함하는 packages/common에 프로젝트를 생성합니다.

공통 코드형 인프라 프로젝트는 다음과 같이 구성됩니다:

  • 디렉터리packages/common/constructs
    • 디렉터리src
      • 디렉터리app/ 프로젝트/생성기에 특정한 인프라를 위한 Constructs
        • …
      • 디렉터리core/ app의 constructs에서 재사용되는 일반 constructs
        • …
      • index.ts app에서 constructs를 내보내는 진입점
    • project.json 프로젝트 빌드 타겟 및 구성

기본 infra: component-version은 누락된 경우 아래의 Greengrass 코어 파일과 컴포넌트당 하나의 앱 래퍼를 추가합니다. 기존 코어 파일은 보존되므로 로컬 편집 내용이 이후 실행에서도 유지됩니다. 디바이스, 사물 그룹 또는 배포를 생성하지 않습니다.

  • 디렉터리packages/common/constructs/src
    • 디렉터리core/greengrass
      • artifact-bucket.ts GreengrassArtifactBucket: grantReadTo가 있는 보존된, 버전 관리된, TLS 전용 리포지토리
      • component-version.ts GreengrassComponentVersion: 아티팩트를 업로드하고 버전을 게시
      • deployment.ts GreengrassDeployment: 배포, 선택적 사물 그룹, 그리고 addComponent
      • next-version.ts NEXT_PATCH, NEXT_MINOR 및 NEXT_MAJOR를 위한 배포 시간 리졸버
      • recipe.ts 합성 시간에 레시피 읽기 및 아티팩트 URI 대체
    • 디렉터리app/greengrass
      • pulse.ts Pulse, 이 컴포넌트의 빌드 출력에 고정된 GreengrassComponentVersion

공유 구성 요소 프로젝트의 build 및 assemble 타겟은 호스트 프로젝트에 종속되므로, 인프라 빌드 또는 배포는 항상 최신 아티팩트를 읽습니다.

컴포넌트 버전은 빌드된 아티팩트를 명시적으로 소유한 리포지토리에 게시합니다. 스택 또는 루트 모듈은 코어 디바이스의 토큰 교환 역할에 읽기 액세스 권한을 부여하고 하나 이상의 배포에 버전을 추가합니다. 기본 읽기 권한은 워크스페이스의 컴포넌트 네임스페이스로 범위가 지정됩니다. 역할의 디바이스가 해당 컴포넌트의 하위 집합만 필요한 경우 범위를 더 좁히세요.

Loading the diagram…

infra = none

infra: none은 레시피, 진입점, Python 테스트, 타겟 및 공유 스크립트만 생성합니다. 인프라 프로젝트나 공유 구성 요소는 생성되거나 변경되지 않습니다. 나중에 infra: component-version으로 다시 실행하여 인프라를 추가할 수 있습니다. 이미 인프라를 소유한 컴포넌트를 none으로 다시 전환할 수는 없습니다.

개발 머신에서 아티팩트를 빌드합니다:

Terminal window
pnpm nx run <project>:pulse-artifact

dist/<project-root>/greengrass/pulse/greengrass-build/ 아래의 출력에는 해결된 레시피가 있는 recipes/와 아카이브가 있는 artifacts/<ComponentName>/<ComponentVersion>/이 포함됩니다. Python 컴포넌트는 매니페스트당 하나의 아카이브를 빌드하며, 두 아키텍처를 모두 타겟으로 하는 경우 <component>-<architecture>.zip으로 명명됩니다. TypeScript 컴포넌트는 모든 매니페스트가 공유하는 하나의 아카이브를 빌드합니다. ipc: true의 경우 각 아키텍처용 aws-crt 애드온을 포함하고 각 매니페스트의 Setenv가 자체 애드온을 선택합니다. 레시피의 아티팩트 URI에 있는 BUCKET_NAME, COMPONENT_NAME 및 COMPONENT_VERSION 플레이스홀더는 게시 시간에 대체되며, 아카이브 기본 이름은 Run 단계가 압축을 푸는 이름으로 유지되어야 합니다.

다음 두 타겟은 일반 워크스테이션이 아닌 클래식 Greengrass nucleus와 greengrass-cli가 설치된 머신에서 실행됩니다:

Terminal window
pnpm nx run <project>:pulse-deploy-local
pnpm nx run <project>:pulse-logs

GREENGRASS_ROOT는 기본적으로 /greengrass/v2입니다. pulse-deploy-local은 로컬 배포를 제출한 다음 컴포넌트의 수명 주기 상태를 폴링하고 세 번의 확인에서 RUNNING 또는 FINISHED 상태를 유지하지 않으면 0이 아닌 값으로 종료합니다. 로컬 배포는 디바이스에서 해당 버전을 고정하고 다른 버전의 이후 클라우드 배포와 충돌합니다. 로컬 및 클라우드 배포를 참조하세요.

infra = component-version

배포 구성은 인프라 프로젝트에 있습니다. 일반적인 흐름은 하나의 리포지토리, 하나의 읽기 권한 부여, 컴포넌트당 하나의 앱 래퍼, 그리고 타겟당 하나의 배포입니다. 기존 사물 그룹 또는 사물에 대해 하나의 배포를 사용하고 targetArn을 설정하세요. 다른 타겟에 대해 다른 배포를 생성하세요.

워크스페이스에 CDK 애플리케이션이 없는 경우 생성합니다:

이 제너레이터 실행@aws/nx-plugin:ts#infra

pnpm nx g @aws/nx-plugin:ts#infra --name=infra
명령 구성하기5

필수

토큰 교환 역할과 타겟 사물 그룹은 계정에 이미 존재합니다: 역할은 코어 디바이스가 가정하는 것이고, 그룹은 디바이스가 등록된 곳입니다. 스택은 CloudFormation 파라미터를 통해 둘 다 가져옵니다.

packages/infra/src/stacks/application-stack.ts
import {
GreengrassArtifactBucket,
GreengrassDeployment,
Pulse,
} from '@my-scope/common-constructs';
import { CfnParameter, Stack, type StackProps } from 'aws-cdk-lib';
import { Role } from 'aws-cdk-lib/aws-iam';
import type { Construct } from 'constructs';
export class ApplicationStack extends Stack {
constructor(scope: Construct, id: string, props?: StackProps) {
super(scope, id, props);
const tokenExchangeRoleArn = new CfnParameter(
this,
'TokenExchangeRoleArn',
{ type: 'String' },
);
const thingGroupArn = new CfnParameter(this, 'ThingGroupArn', {
type: 'String',
});
// One explicitly owned repository, and the grant the devices need.
const artifacts = new GreengrassArtifactBucket(this, 'Artifacts');
const tokenExchangeRole = Role.fromRoleArn(
this,
'TokenExchangeRole',
tokenExchangeRoleArn.valueAsString,
);
artifacts.grantReadTo(tokenExchangeRole);
// The generated wrapper takes the repository construct itself.
const pulse = new Pulse(this, 'Pulse', { bucket: artifacts });
// addComponent records the resolved version and orders the deployment
// resource after the component version resource.
const fleet = new GreengrassDeployment(this, 'Fleet', {
targetArn: thingGroupArn.valueAsString,
});
fleet.addComponent(pulse);
}
}

프로젝트 참조를 동기화하고, 계정과 리전을 한 번 부트스트랩한 다음 배포합니다. deploy 타겟은 ^assemble 후에 인프라 프로젝트에서 cdk deploy --require-approval=never를 실행하며, 이는 먼저 컴포넌트 아티팩트를 다시 빌드합니다. 생성된 main.ts는 모든 스택을 프로젝트 이름을 따서 명명된 스테이지에 배치하므로(@my-scope/infra의 경우 my-scope-infra-sandbox), cdk deploy에는 해당 스테이지에 대한 선택자가 필요합니다. packages/infra/src/main.ts에서 스테이지 이름을 가져와 -- 뒤에 /**와 함께 파라미터 값과 함께 전달하세요:

Terminal window
AWS_PROFILE=<profile> AWS_REGION=<region> pnpm nx sync
pnpm nx run @my-scope/infra:bootstrap
pnpm nx run @my-scope/infra:deploy -- 'my-scope-infra-sandbox/**' --parameters TokenExchangeRoleArn=<role-arn> --parameters ThingGroupArn=<thing-group-arn>

deploy는 완전한 안정화를 기다리고 교체 업데이트를 허용합니다. deploy-sandbox는 익스프레스 모드를 사용하며, --rollback을 전달하지 않으면 교체 업데이트를 거부합니다. 일상적인 수정에는 deploy를 사용하거나, deploy-sandbox를 사용할 때 --rollback을 전달하세요.

fleet.addComponent(pulse)는 필수이며 장식용이 아닙니다. 고정된 ComponentVersion을 사용하면 배포의 컴포넌트 맵에 리터럴 문자열이 포함되므로 CloudFormation은 배포와 컴포넌트 버전 간의 종속성을 인식하지 못하고 병렬로 생성합니다. 그러면 IoT 작업이 버전이 존재하기 전에 디바이스에 도달할 수 있으며, FAILED_NO_STATE_CHANGE로 실패합니다. NEXT_PATCH를 사용하면 맵에 컴포넌트 버전 리소스의 ComponentVersion 속성이 포함되며, 원시 NEXT_* 문자열이 배포에 도달하면 합성이 addComponent를 명명하는 메시지와 함께 실패합니다.

하나의 플릿에 두 개의 컴포넌트

섹션 제목: “하나의 플릿에 두 개의 컴포넌트”

동일하거나 다른 프로젝트에 두 번째 컴포넌트를 생성한 다음 둘 다 하나의 배포에 추가합니다. 재정의는 해당 컴포넌트 항목의 명명된 최상위 필드를 대체합니다. 버전은 항상 구성 요소에서 가져옵니다.

const pulse = new Pulse(this, 'Pulse', { bucket: artifacts });
const uploader = new Uploader(this, 'Uploader', { bucket: artifacts });
const cameras = new GreengrassDeployment(this, 'Cameras', {
thingGroupName: 'cameras',
});
cameras.addComponent(pulse);
cameras.addComponent(uploader, {
configurationUpdate: { merge: { uploadIntervalSeconds: 60 } },
});

thingGroupName은 새 AWS::IoT::ThingGroup을 생성합니다. 디바이스를 등록하지 않습니다. 디바이스는 프로비저닝 프로세스를 통해 그룹에 가입합니다. 이미 존재하는 그룹 또는 사물에는 targetArn 또는 thingName을 사용하세요.

두 개의 플릿에 하나의 컴포넌트

섹션 제목: “두 개의 플릿에 하나의 컴포넌트”

동일한 구성 요소를 두 개의 배포에 등록합니다. 각 배포는 자체 구성을 포함합니다:

new GreengrassDeployment(this, 'WestFleet', {
thingGroupName: 'west-fleet',
}).addComponent(pulse);
new GreengrassDeployment(this, 'EastFleet', {
thingGroupName: 'east-fleet',
}).addComponent(pulse, {
configurationUpdate: { merge: { sampleIntervalSeconds: 60 } },
});

동일한 구성 요소를 하나의 배포에 두 번 추가하는 것은 두 번째 호출이 재정의를 제공하지 않는 한 작동하지 않으며, 재정의는 항목을 업데이트합니다. 동일한 컴포넌트 이름으로 다른 구성 요소를 하나의 배포에 추가하면 구성 시간에 실패합니다.

이 워크스페이스에서 빌드하지 않은 컴포넌트는 components 맵에 들어갑니다. 게시된 공개 컴포넌트는 이미 존재하므로 종속성이 필요하지 않습니다. 동일한 스택에서 생성된 버전에 대한 원시 항목(예: 수동으로 작성된 CfnComponentVersion)에는 addComponent에서 얻을 수 있는 순서 지정 에지가 필요합니다:

import { CfnComponentVersion } from 'aws-cdk-lib/aws-greengrassv2';
const fleet = new GreengrassDeployment(this, 'Fleet', {
targetArn: thingGroupArn.valueAsString,
components: {
// Published by AWS; pick a version from
// `aws greengrassv2 list-component-versions`.
'aws.greengrass.LogManager': {
componentVersion: '2.3.8',
configurationUpdate: {
merge: {
logsUploaderConfiguration: {
systemLogsConfiguration: { uploadToCloudWatch: 'true' },
},
},
},
},
// Created in this stack, so the deployment must wait for it.
'com.example.Handwritten': { componentVersion: '1.2.0' },
},
});
fleet.addComponent(pulse);
const handwritten = new CfnComponentVersion(this, 'Handwritten', {
inlineRecipe: handwrittenRecipe,
});
fleet.deployment.node.addDependency(handwritten);

여기서 configurationUpdate.merge는 일반 객체입니다. 구성 요소는 이를 CloudFormation이 예상하는 JSON 문자열로 직렬화합니다.

하나의 스택이 리포지토리를 소유합니다. 다른 모든 스택은 이름으로 가져오고 소유 스택에 자체 권한 부여를 추가하거나, 토큰 교환 역할 자체에 동등한 s3:GetObject 정책을 연결합니다:

const artifacts = new GreengrassArtifactBucket(this, 'Artifacts', {
existingBucketName: 'central-greengrass-artifacts',
});
const pulse = new Pulse(this, 'Pulse', { bucket: artifacts });

가져온 버킷은 자체 암호화, 버전 관리 및 액세스 설정을 유지합니다. 리졸버는 NEXT_* 버전의 콘텐츠 ID에 버킷 이름을 포함하므로, 컴포넌트를 다른 리포지토리로 이동하면 변경되지 않은 바이트에 대해 새 버전이 할당됩니다.

완료된 스택 업데이트 또는 적용은 배포 리소스가 존재함을 증명합니다. 배포와 IoT 작업이 모두 SUCCEEDED를 보고하는 동안 디바이스의 컴포넌트는 BROKEN일 수 있습니다. 모든 배포 후 디바이스와 컴포넌트 상태를 확인하세요:

터미널 창
aws --profile <profile> --region <region> greengrassv2 get-core-device \
--core-device-thing-name <thing-name> --query status
aws --profile <profile> --region <region> greengrassv2 list-installed-components \
--core-device-thing-name <thing-name>

디바이스가 손상된 상태를 보고하는 경우 <component>-logs를 사용하여 프로세스 로그를 검사하세요.

기본 ipc: false를 사용하면 main.py는 30초마다 한 줄을 로깅하고 레시피에는 IPC 액세스 제어가 없습니다. ipc: true를 사용하면 GreengrassCoreIPCClientV2를 통해 30초마다 컴포넌트 자체의 로컬 토픽에 하트비트를 게시합니다:

greengrass/pulse/main.py
client.publish_to_topic(
topic=TOPIC,
publish_message=PublishMessage(json_message=JsonMessage(message={"sequence": sequence})),
)

레시피는 aws.greengrass.ipc.pubsub 아래 <ComponentName>/heartbeat에 대한 aws.greengrass#PublishToTopic을 부여합니다. 로컬 pub/sub은 AWS IoT Core가 아닌 동일한 코어 디바이스의 다른 컴포넌트에 도달합니다. AWS IoT Core에 게시하려면 publish_to_iot_core를 호출하고, 레시피의 aws.greengrass.ipc.mqttproxy 아래에서 aws.greengrass#PublishToIoTCore를 부여하고, 디바이스의 IoT 정책에 정확한 토픽에 대한 iot:Publish를 제공하세요.

레시피의 액세스 제어는 디바이스의 nucleus에 의해 시행됩니다. SDK만으로는 아무것도 승인하지 않으므로 컴포넌트가 성장함에 따라 operations와 resources를 확장하세요.

생성된 레시피는 클래식 aws.greengrass.Nucleus를 타겟으로 합니다. Nucleus Lite는 IPC 작업의 하위 집합을 구현하고, 플러그인 컴포넌트를 실행하지 않으며, v2.15에서만 컨테이너 지원을 얻었습니다. 플릿이 실행하는 nucleus에 대해 레시피를 확인하세요. 생성기는 nucleus 기능을 감지하지 않습니다.

Ubuntu 25.10 이상에서는 IPC 환경이 손실되기 때문에 sudo-rs가 Greengrass Nucleus 2.18.3으로 IPC 컴포넌트가 시작되는 것을 방지할 수 있습니다. 생성기는 호스트의 sudo 보안 구성을 변경하지 않습니다. IPC가 필요한 경우 클래식 sudo 동작을 제공하는 디바이스 설정을 사용하세요.

CDK 배포의 경우, recipe.yaml의 ComponentVersion을 NEXT_PATCH, NEXT_MINOR 또는 NEXT_MAJOR로 설정하여 플러그인의 리졸버가 계정 및 리전에 이미 게시된 가장 높은 엄격한 시맨틱 버전에서 버전을 할당하도록 요청합니다:

센티널게시된 가장 높은 1.4.2가 되는 값아직 게시되지 않음
NEXT_PATCH1.4.31.0.0
NEXT_MINOR1.5.01.0.0
NEXT_MAJOR2.0.01.0.0

첫 번째 버전은 1.0.0입니다. 리졸버는 콘텐츠 태그가 일치하고 버전이 배포 가능한 경우 게시된 가장 높은 버전을 재사용합니다. 그렇지 않으면 센티널이 선택한 다음 버전을 게시합니다. 태그는 버전 줄이 없는 레시피, 아티팩트 바이트 및 아티팩트 버킷 이름을 포함합니다. 따라서 변경되지 않은 콘텐츠는 버전을 재사용하고, 콘텐츠 또는 버킷을 변경하면 새 버전을 게시합니다. 생성기는 recipe.yaml을 다시 작성하지 않습니다.

NEXT_*는 Nx 플러그인 규칙이지 Greengrass 버전이 아닙니다. CDK는 배포 중에 센티널을 해결하고, <project>:pulse-deploy-local은 디바이스에서 사용 가능한 버전에 대해 해결합니다. GDK는 레시피 필드가 아닌 gdk-config.json의 설정으로 NEXT_PATCH를 사용하며 NEXT_MINOR 또는 NEXT_MAJOR를 지원하지 않습니다. 플러그인은 GDK를 호출하지 않습니다.

Terraform은 명시적 시맨틱 버전이 필요합니다. Greengrass 버전은 불변이므로 변경된 각 아티팩트에는 새 ComponentVersion이 필요합니다. Terraform은 NEXT_*를 해결하지 않습니다.

CDK 리졸버는 스택의 컴포넌트 이름으로 범위가 지정된 생성된 Lambda 실행 역할 아래에서 CloudFormation 배포 중에 실행됩니다. 동일한 컴포넌트에 대해 동시 게시를 실행하지 마세요: 배포가 동일한 다음 버전을 선택하여 하나의 게시가 실패할 수 있습니다.

계정 및 리전당 하나의 아티팩트 리포지토리를 사용하며, 하나의 스택 또는 루트 모듈이 소유합니다. CDK의 existingBucketName 또는 Terraform의 existing_bucket_name으로 다른 곳에서 가져옵니다. 생성된 버킷은 버전 관리되고, SSE-S3로 암호화되며, 비공개이고, TLS가 필요합니다. Greengrass 아티팩트는 CDK 자산이 아닙니다. cdk gc는 컴포넌트 버전에 여전히 필요한 자산을 제거할 수 있습니다.

두 IaC 경로 모두 동일한 S3 객체 키 레이아웃을 사용합니다: <ComponentName>/<first 32 hex characters of sha256>/<file>. 다이제스트는 정렬된 아티팩트 이름과 전체 SHA-256 해시를 해시하며, 하나 또는 여러 아티팩트에 대해 동일한 공식을 사용합니다. 키 접두사만 단축됩니다. 아티팩트 다이제스트와 자동 버전 ID는 전체 해시를 유지합니다. CDK의 버킷 배포 태그는 접두사를 102자로 제한하여 컴포넌트 이름에 68자를 남깁니다. 생성기는 CDK 인프라가 선택될 때 해당 제한을 확인하고, 합성은 레시피 편집을 확인합니다. Terraform의 객체 업로드에는 이 CDK 제한이 없습니다.

배포 또는 롤백이 컴포넌트 버전을 필요로 할 수 있는 동안 게시된 아티팩트를 사용 가능하게 유지하세요. CDK는 기본적으로 교체된 컴포넌트 버전과 해당 업로드를 보존합니다. Terraform은 교체된 버전과 나가는 객체 키를 삭제합니다. 여러 그룹에서 사용하는 버전을 올리기 전에 단계적 롤아웃 및 보존된 버전을 참조하세요. 레지스트리 버전을 제거해도 디바이스에서 이미 실행 중인 컴포넌트는 제거되지 않습니다. Terraform의 리포지토리는 prevent_destroy를 설정합니다. 아티팩트가 더 이상 필요하지 않은 후 의도적인 해체를 위해서만 해당 리터럴 수명 주기 설정을 제거하세요.

기본 토큰 교환 역할 권한 부여는 워크스페이스의 컴포넌트 네임스페이스에서 s3:GetObject를 허용합니다. 더 좁은 권한 부여를 위해 CDK에서 keyPrefix를 설정하거나 Terraform에서 token_exchange_key_prefix를 설정하세요. 가져온 버킷은 자체 정책을 유지하므로 버킷 또는 역할이 소유된 곳에서 권한 부여를 관리하세요.

컴포넌트 버전 또는 배포 구성을 변경하면 배포 리소스가 교체됩니다. CloudFormation은 하나의 스택 작업에서 최대 10개의 배포 리소스를 추가하거나 삭제할 수 있습니다. 대규모 롤아웃을 배치로 분할하세요. 교체에는 일반 deploy 타겟을 사용하세요. deploy-sandbox는 CloudFormation 익스프레스 모드를 사용하며, 교체 업데이트에는 --rollback이 필요합니다.

배포 리소스를 삭제하면 클라우드 관리 상태가 제거되지만 디바이스에서 컴포넌트가 제거되지는 않습니다. 먼저 컴포넌트를 생략하는 수정을 배포하고, list-installed-components에서 사라질 때까지 기다린 다음 IaC 리소스를 제거하세요.

<component>-deploy-local의 로컬 배포는 해당 디바이스에서 컴포넌트 버전을 고정합니다. 다른 버전을 요청하는 클라우드 배포는 고정을 충족할 수 없습니다. 클라우드에서 해당 디바이스를 업데이트하기 전에 로컬 배포를 제거하세요:

터미널 창
sudo /greengrass/v2/bin/greengrass-cli deployment create --remove com.example.Pulse

개발 디바이스에는 로컬 배포를 사용하고 플릿 타겟에는 클라우드 배포를 사용하세요.

클라우드 배포 후 Greengrass 배포 상태와 컴포넌트의 디바이스 상태를 모두 확인하세요:

터미널 창
aws --profile <profile> --region <region> greengrassv2 get-core-device \
--core-device-thing-name <thing-name> --query status
aws --profile <profile> --region <region> greengrassv2 list-installed-components \
--core-device-thing-name <thing-name>

작업이 SUCCEEDED를 보고하는 동안 컴포넌트가 BROKEN일 수 있습니다. <component>-logs를 사용하여 프로세스 로그를 검사하세요. Ubuntu 25.10 이상의 IPC 컴포넌트는 Nucleus 2.18.3의 sudo-rs 아래에서도 실패할 수 있습니다. 생성기는 호스트의 보안 설정을 그대로 둡니다.

Greengrass는 리전당 하나의 컴포넌트를 5,000개 버전으로 제한하고 CreateComponentVersion을 리전당 초당 하나의 요청으로 제한합니다. 레시피는 16KB로 제한되고, 배포 문서는 사물 그룹의 경우 31KB, 단일 사물의 경우 7KB로 제한됩니다. 새 버전을 하트비트로 사용하지 말고 대규모 배포를 더 작은 배치로 분할하세요.