come utilizzare Greengrass per fare il deploy di funzioni long-running e on-demand<\/a>. <\/p>\n\n\n\nSiamo sicuri che la soluzione possa fare al caso nostro, ci manca per\u00f2 trovare un modo per evitare di configurare manualmente ogni singola virtual machine. <\/p>\n\n\n\n
Per nostra fortuna, Greengrass Fleet provisioning \u00e8 la soluzione che stiamo cercando: permette di fare deploy massivi di edge device, rendendo sicuro il processo di onboarding. <\/p>\n\n\n\n
Per fare il provisioning automatico di una flotta di dispositivi dobbiamo per prima cosa trovare un meccanismo che permetta di autenticare la richiesta di onboard, altrimenti chiunque potrebbe aggiungere un dispositivo e, potenzialmente, causare danni. <\/p>\n\n\n\n
Una volta fatta la prima autenticazione, al device viene assegnato un certificato “definitivo” che gli permetterr\u00e0 di essere riconosciuto.<\/p>\n\n\n\n
Esistono due meccanismi di autenticazione: claim<\/strong> e trusted users<\/strong>. Come vedremo, il loro utilizzo dipende dal caso d’uso. <\/p>\n\n\n\nIl provisioning mediante “trusted user” \u00e8 solitamente utilizzato quando \u00e8 richiesta l’interazione dell’utente, ad esempio per dispositivi wi-fi smart controllati da applicazioni mobili. <\/p>\n\n\n\n
La richiesta \u00e8 fatta con un utente per conto del dispositivo. Il certificato definitivo, una volta ottenuto, dovr\u00e0 essere caricato sul device.<\/p>\n\n\n\n
Il provisioning utilizzando claim fa in modo che sia direttamente il dispositivo ad effettuare la richiesta, utilizzando per l’autenticazione certificati “base” gi\u00e0 installati. Questo scenario non richiede interazione da parte dell’utente, per cui i deploy massivi sono il caso d’uso perfetto. <\/p>\n\n\n\n
Eureka! Abbiamo la nostra soluzione: useremo AWS IoT Greengrass Core con il plugin per il fleet provisioning<\/strong>.<\/p>\n\n\n\nPer prima cosa approfondiamo i passi necessari. Per usare il provisioning con claim occorre:<\/p>\n\n\n\n
\nCreare un certificato e, mediante una policy, autorizzarlo ad ottenere nuovi certificati<\/li>\n\n\n\n Creare un template in AWS IoT Core che definisca i dettagli identificativi del dispositivo (come ad esempio nome, gruppo, numero di serie)<\/li>\n\n\n\n Installare il software Greengrass su una macchina che sar\u00e0 usata come template<\/li>\n\n\n\n Configurare il plugin per il fleet provisioning e personalizzare il deployment<\/li>\n\n\n\n Iniziare a fare il deploy di tutti i dispositivi <\/li>\n<\/ol>\n\n\n\nSiccome siamo pigri, useremo CloudFormation e script per automatizzare il pi\u00f9 possibile. Come bonus, creeremo una virtual machine di riferimento, installando tutti i componenti che andremo ad usare.<\/p>\n\n\n\n
Per prima cosa, creiamo uno script per creare e salvare su SecretsManager i certificati per il provisioning. In questo modo, riusciremo ad automatizzare il loro deploy sulla macchina virtuale<\/p>\n\n\n\n
#!\/bin\/bash\nENV=${1}\n\n\nSECRETSMANAGER_PREFIX=\"MyAwesomeProjectSecret\"\nTHINGGROUP_NAME=\"MyThingGroup\"\n\n# Create the certificate\nCERTIFICATE_ARN=$(aws iot create-keys-and-certificate \\\n \t--certificate-pem-outfile \"claim-certs\/claim.pem.crt\" \\\n \t--public-key-outfile \"claim-certs\/claim.public.pem.key\" \\\n \t--private-key-outfile \"claim-certs\/claim.private.pem.key\" \\\n \t--set-as-active | jq -r .certificateArn)\n\nSECRET_STRING=$(echo \"$(cat claim-certs\/claim.pem.crt)|$(cat claim-certs\/claim.private.pem.key)\" | tr '\\n' ';' )\naws secretsmanager create-secret --name $SECRETSMANAGER_PREFIX\/$ENV\/claims-certificate --secret-string $SECRET_STRING\n\n# Attach the policy to the certificate\naws iot attach-policy --policy-name GreengrassProvisioningClaimPolicy --target $CERTIFICATE_ARN\naws iot create-thing-group --thing-group-name $THINGGROUP_NAME<\/code><\/pre>\n\n\n\nQuesto script va usato prima di fare il deploy del template CloudFormation che segue, che si occuper\u00e0 di creare il grouppo, le policy da associare con i certificati ed i ruoli necessari.<\/p>\n\n\n\n
---\nAWSTemplateFormatVersion: '2010-09-09'\nDescription: GreenGrass with Fleet Manager provisioning template\n\nMetadata:\n\n 'AWS::CloudFormation::Interface':\n\tParameterGroups:\n \t- Label: {default: 'Required parameters'}\n \tParameters:\n \t- Env\n \t- ThingGroupName\n \t- Label: {default: 'Optional parameters'}\n \tParameters:\n \t- ProjectName\n\nParameters:\n\n Env:\n\tType: String\n\tDescription: \"Insert the environment\"\n\n ProjectName:\n\tType: String\n\tDescription: \"Insert the name of the project\"\n\n ThingGroupName:\n\tType: String\n\tDescription: \"Insert the Thing Group name\"\n\nResources:\n\n GreengrassTokenExchangeRole:\n\tType: AWS::IAM::Role\n\tProperties:\n \tAssumeRolePolicyDocument:\n \tVersion: '2012-10-17'\n \tStatement:\n \t- Effect: 'Allow'\n \tPrincipal:\n \tService:\n \t- 'credentials.iot.amazonaws.com'\n \tAction:\n \t- 'sts:AssumeRole'\n \t- Effect: 'Allow'\n \tPolicies:\n \t- PolicyName: 'LogPersmission'\n \tPolicyDocument:\n \tVersion: '2012-10-17'\n \tStatement:\n \t- Effect: 'Allow'\n \tAction:\n \t- \"logs:CreateLogGroup\"\n \t- \"logs:CreateLogStream\"\n \t- \"logs:PutLogEvents\"\n \t- \"logs:DescribeLogStreams\"\n \t- \"s3:GetBucketLocation\"\n \t- \"iam:PassRole\"\n \tResource: '*'\n \t \tRoleName: !Sub '${ProjectName}-${Env}-V2TokenExchangeRole'\n\n GreengrassTokenExchangeRoleAlias:\n\tType: AWS::IoT::RoleAlias\n\tProperties:\n \tRoleAlias: !Sub '${ProjectName}-${Env}-CoreTokenExchangeRoleAlias'\n \tRoleArn: !GetAtt GreengrassTokenExchangeRole.Arn\n \tTags:\n \t- Key: Name\n \tValue: !Sub \"${ProjectName}-${Env}-CoreTokenExchangeRoleAlias\"\n\n GreengrassV2IoTThingPolicy:\n\tType: AWS::IoT::Policy\n\tProperties:\n \tPolicyDocument:\n \tVersion: '2012-10-17'\n \tStatement:\n \t- Effect: 'Allow'\n \tAction:\n \t- \"iot:Publish\"\n \t- \"iot:Subscribe\"\n \t- \"iot:Receive\"\n \t- \"iot:Connect\"\n \t- \"greengrass:*\"\n \tResource: \"*\"\n \t- Effect: 'Allow'\n \tAction:\n \t- \"iot:AssumeRoleWithCertificate\"\n \tResource: !GetAtt GreengrassTokenExchangeRoleAlias.RoleAliasArn\n \tPolicyName: V2IoTThingPolicy\n\n GreengrassFleetProvisioningRole:\n\tType: AWS::IAM::Role\n\tProperties:\n \tAssumeRolePolicyDocument:\n \tVersion: '2012-10-17'\n \tStatement:\n \t- Effect: 'Allow'\n \tPrincipal:\n \tService:\n \t- 'iot.amazonaws.com'\n \tAction:\n \t- 'sts:AssumeRole'\n \tPolicies:\n \t- PolicyName: 'ECRAccess'\n \tPolicyDocument:\n \tVersion: '2012-10-17'\n \tStatement:\n \t- Effect: 'Allow'\n \tAction:\n \t- \"logs:CreateLogGroup\"\n \t- \"logs:CreateLogStream\"\n \t- \"logs:PutLogEvents\"\n \t- \"logs:DescribeLogStreams\"\n \t- \"s3:GetBucketLocation\"\n \tResource: '*'\n \tRoleName: !Sub '${ProjectName}-${Env}-FleetProvisioningRole'\n\n GreengrassFleetProvisioningTemplate:\n\tType: AWS::IoT::ProvisioningTemplate\n\tProperties:\n \tDescription: \"template to provision iot fleet resources\"\n \tEnabled: true\n \tProvisioningRoleArn: !GetAtt GreengrassFleetProvisioningRole.Arn\n \tTemplateBody: |\n \t{\n \t\"Parameters\": {\n \t\"ThingName\": {\n \t\"Type\": \"String\"\n \t},\n \t\"ThingGroupName\": {\n \t\"Type\": \"String\"\n \t},\n \t\"AWS::IoT::Certificate::Id\": {\n \t\"Type\": \"String\"\n \t}\n \t},\n \t\"Resources\": {\n \t\"MyThing\": {\n \t\"OverrideSettings\": {\n \t\"AttributePayload\": \"REPLACE\",\n \t\"ThingGroups\": \"REPLACE\",\n \t\"ThingTypeName\": \"REPLACE\"\n \t},\n \t\"Properties\": {\n \t\"AttributePayload\": {},\n \t\"ThingGroups\": [\n \t{\n \t\"Ref\": \"ThingGroupName\"\n \t}\n \t],\n \t\"ThingName\": {\n \t\"Ref\": \"ThingName\"\n \t}\n \t},\n \t\"Type\": \"AWS::IoT::Thing\"\n \t},\n \t\"MyPolicy\": {\n \t\"Properties\": {\n \t\"PolicyName\": \"V2IoTThingPolicy\"\n \t},\n \t\"Type\": \"AWS::IoT::Policy\"\n \t},\n \t\"MyCertificate\": {\n \t\"Properties\": {\n \t\"CertificateId\": {\n \t\"Ref\": \"AWS::IoT::Certificate::Id\"\n \t},\n \t\"Status\": \"Active\"\n \t},\n \t\"Type\": \"AWS::IoT::Certificate\"\n \t}\n \t}\n \t}\n \tTemplateName: !Sub \"${ProjectName}-${Env}-FleetTemplate\"\n\n GreengrassProvisioningClaimPolicy:\n\tType: AWS::IoT::Policy\n\tProperties:\n \tPolicyDocument:\n \tVersion: '2012-10-17'\n \tStatement:\n \t- Effect: 'Allow'\n \tAction:\n \t- \"iot:Connect\"\n \tResource: \"*\"\n \t- Effect: 'Allow'\n \tAction:\n \t- \"iot:Subscribe\"\n \tResource:\n \t- !Sub \"arn:aws:iot:${AWS::Region}:${AWS::AccountId}:topicfilter\/$aws\/certificates\/create\/*\"\n \t- !Sub \"arn:aws:iot:${AWS::Region}:${AWS::AccountId}:topicfilter\/$aws\/provisioning-templates\/${GreengrassFleetProvisioningTemplate}\/provision\/*\"\n \t- Effect: 'Allow'\n \tAction:\n \t- \"iot:Publish\"\n \t- \"iot:Receive\"\n \tResource:\n \t- !Sub \"arn:aws:iot:${AWS::Region}:${AWS::AccountId}:topic\/$aws\/certificates\/create\/*\"\n \t- !Sub \"arn:aws:iot:${AWS::Region}:${AWS::AccountId}:topic\/$aws\/provisioning-templates\/${GreengrassFleetProvisioningTemplate}\/provision\/*\"\n \tPolicyName: GreengrassProvisioningClaimPolicy<\/code><\/pre>\n\n\n\nLa risorsa <\/p>\n\n\n\n
GreengrassFleetProvisioningTemplate <\/code><\/pre>\n\n\n\nfar\u00e0 in modo che il nostro IoT device si registrer\u00e0 usando le propriet\u00e0 GroupName<\/strong> e ThingName<\/strong>. Le propriet\u00e0 sono personalizzabili a seconda delle necessit\u00e0. In seguito vedremo che “ThingName” sar\u00e0 popolato con il valore dell’hostname della macchina. <\/p>\n\n\n\nPer ricevere una notifica ed eseguire azioni in modo automatico quando un dispositivo si registra, \u00e8 possibile aggiungere una regola come quella che segue, aggiungendo anche un topic SNS (non includeremo il codice completo per brevit\u00e0):<\/p>\n\n\n\n
ThingCreationIotRule:\n\tType: AWS::IoT::TopicRule\n\tProperties:\n \tRuleName: !Sub '${ProjectName}_${Env}_greengrass_rule'\n \tTopicRulePayload:\n \tRuleDisabled: 'false'\n \tSql: SELECT * FROM '$aws\/events\/thing\/#'\n \tActions:\n \t- Lambda:\n \tFunctionArn: !GetAtt RegisterThingLambda.Arn\n \tErrorAction:\n \tSns:\n \tTargetArn: !Ref SNSFailedNotificationTopic\n \tRoleArn: !GetAtt NotificationRole.Arn<\/code><\/pre>\n\n\n\n\u00c8 finalmente arrivata l’ora di creare una macchina virtuale. Useremo Ubuntu 22.04 per la base, va benissimo usare VirtualBox o Vmware.<\/p>\n\n\n\n
Lo script che segue si occupa di installare il software e ottenere i certificati da usare per il claim provisioning. Per usarlo occorre ottenere credenziali temporanee IAM come variabili di ambiente o, ancora meglio, integrare tutto in un template packer.<\/p>\n\n\n\n
#!\/bin\/bash\n\nENV=${1}\n\nSECRETSMANAGER_PREFIX=\"MyAwesomeProjectSecret\"\n\nmkdir -p claim-certs\nSECRET_STRING=$(aws secretsmanager get-secret-value --secret-id $SECRETSMANAGER_PREFIX\/$ENV\/claims-certificate --query SecretString --output text)\n\necho $SECRET_STRING | cut -d '|' -f 1 | tr ';' '\\n' > claim-certs\/claim.pem.crt\necho $SECRET_STRING | cut -d '|' -f 2 | tr ';' '\\n' > claim-certs\/claim.private.pem.key\n\n\nexport DEBIAN_FRONTEND=noninteractive\napt update && apt upgrade -y\napt -yq install python3-pip zip open-vm-tools default-jdk libgl1-mesa-glx\n\n\nmkdir -p \/greengrass\/v2\/installer\nmkdir \/greengrass\/v2\/claim-certs\n\n\nmv claim-certs\/claim.private.pem.key \/greengrass\/v2\/claim-certs\/\nmv \/claim-certs\/claim.pem.crt \/greengrass\/v2\/claim-certs\/\n\n\n\ncd \/greengrass\/v2\ncurl -o \/greengrass\/v2\/AmazonRootCA1.pem https:\/\/www.amazontrust.com\/repository\/AmazonRootCA1.pem\ncurl -s https:\/\/d2s8p88vqu9w66.cloudfront.net\/releases\/greengrass-nucleus-latest.zip > greengrass-nucleus-latest.zip\nunzip greengrass-nucleus-latest.zip -d installer && rm greengrass-nucleus-latest.zip\ncurl -s https:\/\/d2s8p88vqu9w66.cloudfront.net\/releases\/aws-greengrass-FleetProvisioningByClaim\/fleetprovisioningbyclaim-latest.jar > installer\/aws.greengrass.FleetProvisioningByClaim.jar<\/code><\/pre>\n\n\n\nTutti i componenti necessari sono ora installati. Ora manca l’ultimo tocco del sysadmin pigro: vogliamo qualcosa che, una volta avviata, si configuri in autonomia senza dover intervenire manualmente. <\/p>\n\n\n\n
Faremo in modo che il campo “ThingName” sia popolato automaticamente con l’hostname grazie ad una unit systemd ed uno script. Basta creare un file config.yml in <\/p>\n\n\n\n
greengrass\/v2\/installer\/ <\/code><\/pre>\n\n\n\nspecificando che useremo il plugin IoT fleet provisioning.<\/p>\n\n\n\n
---\nservices:\n aws.greengrass.Nucleus:\n\tversion: \"2.9.1\"\n aws.greengrass.FleetProvisioningByClaim:\n\tconfiguration:\n \trootPath: \"\/greengrass\/v2\"\n \tawsRegion: \"eu-west-1\"\n \tiotDataEndpoint: \"endpoint.iot.region.amazonaws.com\"\n \tiotCredentialEndpoint: \"endpoint.credentials.iot.region.amazonaws.com\"\n \tiotRoleAlias: \"myproject-environment-CoreTokenExchangeRoleAlias\"\n \tprovisioningTemplate: \"myproject-environment-FleetTemplate\"\n \tclaimCertificatePath: \"\/greengrass\/v2\/claim-certs\/claim.pem.crt\"\n \tclaimCertificatePrivateKeyPath: \"\/greengrass\/v2\/claim-certs\/claim.private.pem.key\"\n \trootCaPath: \"\/greengrass\/v2\/AmazonRootCA1.pem\"\n \ttemplateParameters:\n \tThingName: \"REPLACEME\"\n \tThingGroupName: \"MyThingGroup\"<\/code><\/pre>\n\n\n\nQuesto file va adattato con i valori corretti. Il valore di <\/p>\n\n\n\n
iotDataEndpoint<\/code><\/pre>\n\n\n\n e <\/p>\n\n\n\n
iotCredentialEndpoint <\/code><\/pre>\n\n\n\ncon questi comandi:<\/p>\n\n\n\n
aws iot describe-endpoint --endpoint-type iot:Data-ATS\naws iot describe-endpoint --endpoint-type iot:CredentialProvider<\/code><\/pre>\n\n\n\nIl valore di ThingName \u00e8 impostato a “REPLACEME<\/strong>” Lo script <\/p>\n\n\n\n(\/greengrass\/configure-device.sh)<\/code><\/pre>\n\n\n\nsi occuper\u00e0 di sostituire il valore e un servizio systemd sar\u00e0 eseguito al primo avvio.<\/p>\n\n\n\n
#!\/bin\/bash\n\nset -e\n\nsed -i s\/REPLACEME\/$(hostname)\/g \/greengrass\/v2\/installer\/config.yaml\necho \"********************** RUN SERVICE **************************\"\njava -Droot=\"\/greengrass\/v2\" -Dlog.store=FILE \\\n-jar \/greengrass\/v2\/installer\/lib\/Greengrass.jar \\\n--trusted-plugin \/greengrass\/v2\/installer\/aws.greengrass.FleetProvisioningByClaim.jar \\\n--init-config \/greengrass\/v2\/installer\/config.yaml \\\n--component-default-user ggc_user:ggc_group \\\n--setup-system-service true<\/code><\/pre>\n\n\n\nLa prima riga (sed -i s\/REPLACEME\/$(hostname)\/g \/greengrass\/v2\/installer\/config.yaml) sostituisce il valore con l’hostname.<\/p>\n\n\n\n
Ora basta creare una unit nella directory \/lib\/systemd\/system con un nome parlante (abbiamo usato greengrass-config.service)<\/p>\n\n\n\n
[Unit]\nBefore=systemd-user-sessions.service\nWants=network-online.target\nAfter=network-online.target\nConditionPathExists=!\/greengrass\/greengrass_installed\n\n[Service]\nType=oneshot\nExecStart=\/greengrass\/configure-device.sh\nExecStartPost=\/usr\/bin\/touch \/greengrass\/greengrass_installed\nRemainAfterExit=yes\n\n[Install]\nWantedBy=multi-user.target<\/code><\/pre>\n\n\n\nSi tratta di una unit systemd quasi standard, con due piccole particolarit\u00e0:<\/p>\n\n\n\n
La riga <\/p>\n\n\n\n
ConditionPathExists=!\/greengrass\/greengrass_installed <\/code><\/pre>\n\n\n\nfa si che systemd esegua il comando solo se il file non esiste<\/p>\n\n\n\n
ExecStartPost=\/usr\/bin\/touch \/greengrass\/greengrass_installed <\/code><\/pre>\n\n\n\nfa si che il file sia creato. In questop modo il serivizo sar\u00e0 eseguito solo una volta. <\/p>\n\n\n\n
Non ci resta che attivare ed abilitare il tutto con:<\/p>\n\n\n\n
systemctl daemon-reload\nsystemctl enable greengrass-config.service<\/code><\/pre>\n\n\n\nFinita la configurazione, basta esportare l’immagine della macchina virtuale in un formato compatibile con l’hypervisor (OVA o template VMware) ed iniziare a fare il deploy massivo! <\/p>\n\n\n\n
Un ultimo appunto: questa \u00e8 una configurazione di base, \u00e8 necessario fare in modo che sia aggiornata ed ottimizzata (ad esempio si pu\u00f2 ridurre la dimensione del disco disinstallando e disabilitando servizi non essenziali)<\/p>\n\n\n\n
Al termine di questo percorso possiamo finalmente eseguire long-running functions usando le risorse inutilizzabili at the edge.<\/p>\n\n\n\n
\u00c8 possibile integrare altri servizi AWS usando questa soluzione come base: i job SageMaker sono un buon esempio (e magari uno spunto che approfondiremo in futuro). <\/p>\n\n\n\n
Abbiamo visto come l’automazione ci pu\u00f2 aiutare liberandoci dal dover eseguire compiti noiosi, anche se, a prima vista, pu\u00f2 sembrare complesso. <\/p>\n\n\n\n
Vi \u00e8 mai capitato di farvi prendere dalla frenesia dell’automazione per troppa pigrizia? <\/p>\n\n\n\n
Fatecelo sapere nei commenti!<\/p>\n\n\n\n
Ci vediamo tra 14 giorni su #Proud2beCloud<\/strong><\/p>\n\n\n\n \n\n\n\nAbout Proud2beCloud<\/h4>\n\n\n\n Proud2beCloud \u00e8 il blog di beSharp<\/a>, APN Premier Consulting Partner italiano esperto nella progettazione, implementazione e gestione di infrastrutture Cloud complesse e servizi AWS avanzati. Prima di essere scrittori, siamo Solutions Architect che, dal 2007, lavorano quotidianamente con i servizi AWS. Siamo innovatori alla costante ricerca della soluzione pi\u00f9 all’avanguardia per noi e per i nostri clienti. Su Proud2beCloud condividiamo regolarmente i nostri migliori spunti con chi come noi, per lavoro o per passione, lavora con il Cloud di AWS. Partecipa alla discussione!<\/p>\n","protected":false},"excerpt":{"rendered":"Dammi sei ore per abbattere un albero e spender\u00f2 le prime quattro ore per affilare l\u2019ascia. Abraham Lincoln. Lo confesso: […]<\/p>\n","protected":false},"author":13,"featured_media":5407,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[477],"tags":[575,412],"yoast_head":"\n
Come fare il setup di migliaia di dispositivi su AWS Greengrass e vivere felici - Proud2beCloud Blog<\/title>\n \n \n \n \n \n \n \n \n \n \n \n \n\t \n\t \n\t \n \n \n \n \n \n \n\t \n\t \n\t \n