# AWS IoT Greengrass V2 Workshop 操作流程 https://catalog.workshops.aws/greengrass/en-US # 下載資源 CloudFormation Template https://tinyurl.com/yvf5y3zw # Chapter 2 [Reference](https://catalog.workshops.aws/greengrass/en-US/chapter2-letsbegin) ## Chapter 2.1 建立 EC2 環境 ### 建立 EC2 Key-Pair * 進入 EC2 Console -> [Key pairs](https://ap-northeast-1.console.aws.amazon.com/ec2/home?region=ap-northeast-1#KeyPairs:),點擊右方橘色 Create key pair ![Screenshot 2024-10-03 at 10.00.23 AM](https://hackmd.io/_uploads/HkYnsdiAR.png) * 自行命名 Key Pair,例如:**20241004-gg-ws-kp**,點擊右下方橘色 Create key pair ![Screenshot 2024-10-03 at 11.03.11 AM](https://hackmd.io/_uploads/rJuKKKjCA.png) * 此時會自動下載 pem 檔 ![Screenshot 2024-10-03 at 11.03.52 AM](https://hackmd.io/_uploads/Hy3CKKo0C.png) * 看到 Key pairs 列表中出現剛剛建立的 key pair 即表示成功。 ![Screenshot 2024-10-03 at 11.04.15 AM](https://hackmd.io/_uploads/SkWk9YiRR.png) ### 建立模擬 Edge Gateway 的 EC2 * 利用 CloudFormation template的方式自動建立 EC2 * 下載CloudFormation Template:[nhoatcc-gg-imd.yaml](https://tinyurl.com/yvf5y3zw) * 前往 CloudFormation [Console](https://ap-northeast-1.console.aws.amazon.com/cloudformation/home?region=ap-northeast-1#/stacks?filteringText=&filteringStatus=active&viewNested=true) ![Screenshot 2024-10-03 at 11.08.25 AM](https://hackmd.io/_uploads/SkYh9FsRA.png) * 點擊橘色 Create stack 按鈕 ![Screenshot 2024-10-03 at 11.09.24 AM](https://hackmd.io/_uploads/ByDesFiAR.png) * 選擇 Upload a template file 並選擇下載好的 yaml 檔 (nhoatcc-gg-imd.yaml) * 完成後按下橘色 Next ![Screenshot 2024-10-03 at 11.16.13 AM](https://hackmd.io/_uploads/S1SjhKjRR.png) * 可自行命名 Stack Name,EX:**GreengrassV2Workshop** * 下拉選擇 KeyName,選擇前步驟建立的 Key pair * 都完成後點擊橘色 Next * 在 Step 3 Configure stack options 時保持預設值,並下滑到最底後,按下橘色 Next * Step 4 Review and create 階段,下滑到最底。 ![Screenshot 2024-10-03 at 11.19.33 AM](https://hackmd.io/_uploads/ByBL6Yi0C.png) * 勾選 **I acknowledge that AWS CloudFormation might create IAM resources.** * 點擊橘色 Submit ![Screenshot 2024-10-03 at 11.20.40 AM](https://hackmd.io/_uploads/r1OcpYjRA.png) * EC2 開始建置,待 Status 轉換為 CREATE_COMPLETE 後,選擇 Outputs 分頁 ![Screenshot 2024-10-03 at 11.46.49 AM](https://hackmd.io/_uploads/SJyJVciC0.png) * Outputs 中的 SSHCommand 可以直接利用 ssh 進行連線 ``` ssh -i 20241004-gg-ws-kp.pem ubuntu@ec2-57-181-31-78.ap-northeast-1.compute.amazonaws.com ``` * 後面我們會利用 VSC 進行SSH連線,並直接對 EC2 上的程式碼進行編輯。 * 參考文件:https://medium.com/@christyjacob4/using-vscode-remotely-on-an-ec2-instance-7822c4032cff ### 建立 VSC 與 EC2 SSH 連線 * 下載VSC SSH套件 [Remote - SSH](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-ssh) * 點擊:Open Visual Studio Code ![Screenshot 2024-10-03 at 11.53.35 AM](https://hackmd.io/_uploads/H14vH9i00.png) * 安裝:Visual Studio Code Remote - SSH ![Screenshot 2024-10-03 at 11.53.54 AM](https://hackmd.io/_uploads/SyJiBcsAA.png) ![Screenshot 2024-10-03 at 11.56.01 AM](https://hackmd.io/_uploads/Hym1vcjAC.png) * 安裝完成後點擊左下角 ><符號 ![Screenshot 2024-10-03 at 11.56.10 AM](https://hackmd.io/_uploads/ryX1DcjAA.png) * 點擊 Connect to Host... ![Screenshot 2024-10-03 at 11.58.01 AM](https://hackmd.io/_uploads/S17kw5iCR.png) * 點擊 Configure SSH Hosts... ![Screenshot 2024-10-03 at 11.58.13 AM](https://hackmd.io/_uploads/ryXyP9sAR.png) * 點擊當前 user 的 config檔案,EX: /Users/mayawc/.ssh/config ![Screenshot 2024-10-03 at 12.08.40 PM](https://hackmd.io/_uploads/S1xyKco0A.png) * HostName/User 即為前面 Outposts 中取得的SSHCommand裡的資訊,EX: * User: ubuntu * HostName: ec2-57-181-31-78.ap-northeast-1.compute.amazonaws.com * IdentityFile 則是您下載儲存 20241004-gg-ws-kp.pem 的絕對路徑 * 另外基於安全性的要求,AWS EC2 SSH要求pem檔案的權限必須限縮,之後才能連線。需要執行chmod將權限限縮 ![Screenshot 2024-10-03 at 12.07.07 PM](https://hackmd.io/_uploads/H1gKdcjRC.png) ``` chmod 400 20241004-gg-ws-kp.pem ``` ### 進行 VSC 與 EC2 SSH 連線 * 點擊 VSC 左下角,並選擇 Connect to Host... ![Screenshot 2024-10-03 at 12.15.24 PM](https://hackmd.io/_uploads/ryZ295sRA.png) * 選擇剛剛建立的 SSH Config: NhoaGGWorkshop ![Screenshot 2024-10-03 at 12.15.35 PM](https://hackmd.io/_uploads/SJZnq9jCC.png) * 此時會跳出一個新的VSC視窗,左下角顯示 SSH:NhoaGGWorkshop 即表示正確連線 ![Screenshot 2024-10-03 at 12.15.57 PM](https://hackmd.io/_uploads/BJWnq9iAR.png) * 點擊 Open (資料夾),在 /home/ubuntu/ 按下 OK ![Screenshot 2024-10-03 at 12.16.09 PM](https://hackmd.io/_uploads/SJ-359jRA.png) * 左側看見EXPLORER將檔案全部載入,表示連線與設定成功,可以開始 Greengrass V2 Workshop 內容。 ![Screenshot 2024-10-03 at 12.16.19 PM](https://hackmd.io/_uploads/HyWh55oAC.png) ## Chapter 2.2 建立 IAM User CLI credential * 目標:建立 Greengrass 安裝部署人員所需的 IAM User credential,提供CLI指令所需要的權限。 * 我們會建立一個名為:workshop_user_delete_later 的 admin user。 * 在未來 Production 環境下,我們有建議的[最佳實踐權限設定範圍](https://docs.aws.amazon.com/greengrass/v2/developerguide/provision-minimal-iam-policy.html) ### 建立一個 IAM User * 前往 AWS IAM [console](https://us-east-1.console.aws.amazon.com/iam/home?region=ap-northeast-1) * 點擊橘色 Create user ![Screenshot 2024-10-03 at 12.26.20 PM](https://hackmd.io/_uploads/SymSeijCR.png) * Step1: 命名 IAM User:workshop_user_delete_later,完成後點擊橘色 Next ![Screenshot 2024-10-03 at 12.26.30 PM](https://hackmd.io/_uploads/HJ7HesoAA.png) * Step2: 選擇 Attach policies directly,在搜尋欄填入 Admin,接著勾選 AdministratorAccess,完成後下滑點擊橘色 Next ![Screenshot 2024-10-03 at 12.26.44 PM](https://hackmd.io/_uploads/H1mrljiAR.png) * Step3: 確認無誤後點擊橘色 Create user ![Screenshot 2024-10-03 at 12.39.57 PM](https://hackmd.io/_uploads/rkQBljiR0.png) * 跳轉回 IAM User 列表後,點擊剛剛建立的 workshop_user_delete_later ![Screenshot 2024-10-03 at 12.45.17 PM](https://hackmd.io/_uploads/S1ytbsoRA.png) ### 取得 CLI Credential * 點擊 Security credentials 分頁 ![Screenshot 2024-10-03 at 12.54.27 PM](https://hackmd.io/_uploads/rJXyriiRA.png) * 點擊 Create access key ![Screenshot 2024-10-03 at 12.54.37 PM](https://hackmd.io/_uploads/SJmkSjoCC.png) * Use case: Command Line Interface (CLI),記得勾選底下confirmation後點擊橘色 Next ![Screenshot 2024-10-03 at 12.54.53 PM](https://hackmd.io/_uploads/SkQyHoiRA.png) * 點擊橘色 Create access key ![Screenshot 2024-10-03 at 12.55.07 PM](https://hackmd.io/_uploads/HymJrii00.png) * Retrieve access keys * Download .csv file (避免日後忘記) * 複製 Access key 以及 Secret access key(點擊show) ![Screenshot 2024-10-03 at 12.57.33 PM](https://hackmd.io/_uploads/SJQ1SsoRA.png) ### 將 Access Key Secret Key (AKSK) 設定到 EC2 (Edge Gateway) 環境 #### aws configure * 回到 VSC Terminal ![Screenshot 2024-10-03 at 1.06.49 PM](https://hackmd.io/_uploads/HkQiUjiAR.png) * 先執行 aws sts get-caller-identity 指令,我們可以確認,目前這台新安裝的EC2上,並沒有任何AWS的權限,為了我們接下來要進行的Greengrass安裝,我們需要讓它具有 AWS IAM 的權限。 * 利用 aws configure 這個指令,我們可以將前面建立的 aksk 設定到這台EC2的環境。 * 依照 aws configure 的提示,將資訊依序輸入。 ``` ubuntu@ip-10-0-1-21:~$ aws sts get-caller-identity Unable to locate credentials. You can configure credentials by running "aws configure". ubuntu@ip-10-0-1-21:~$ aws configure AWS Access Key ID [None]: ************************** AWS Secret Access Key [None]: ************************** Default region name [None]: ap-northeast-1 Default output format [None]: json ubuntu@ip-10-0-1-21:~$ aws sts get-caller-identity { "UserId": "AIDA5IPFWS7QCA4KORNVF", "Account": "911551666144", "Arn": "arn:aws:iam::911551666144:user/workshop_user_delete_later" } ubuntu@ip-10-0-1-21:~$ ``` * 最後我們再用 aws sts get-caller-identity 指令檢查一次,可以看到他指出我們現在具有 workshop_user_delete_later 這個 IAM User的身份了,也具有他被賦予的權限。 #### export * 接下來在環境變數 export ``` export AWS_DEFAULT_REGION="ap-northeast-1" export AWS_ACCESS_KEY_ID="Access Key" export AWS_SECRET_ACCESS_KEY="Secret Key" ``` * 確認正確export ``` echo $AWS_DEFAULT_REGION echo $AWS_ACCESS_KEY_ID echo $AWS_SECRET_ACCESS_KEY ``` ### 建立 Greengrass Servoce 在雲端所需的權限 [參考步驟](https://docs.aws.amazon.com/greengrass/v2/developerguide/greengrass-service-role.html#create-service-role) * 這個權限的建立,可以使用前面的 IAM Console,也可以使用 aws cli 直接在模擬 Edge Gateway的 EC2 Terminal 上執行。 * 由於這個EC2 上的 IAM User (workshop_user_delete_later) 擁有 admin 權限,我們就直接利用它來練習使用 aws cli 建立 IAM Role * 複製並且執行以下[指令](https://docs.aws.amazon.com/greengrass/v2/developerguide/greengrass-service-role.html#create-service-role),建立名為Greengrass_ServiceRole的 IAM Role ``` aws iam create-role --role-name Greengrass_ServiceRole --assume-role-policy-document '{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "Service": "greengrass.amazonaws.com" }, "Action": "sts:AssumeRole", "Condition": { "ArnLike": { "aws:SourceArn": "arn:aws:greengrass:region:account-id:*" }, "StringEquals": { "aws:SourceAccount": "account-id" } } } ] }' ``` * 記下此 Role 的 ARN 名稱,在response的json當中 ``` { "Role": { "Path": "/", "RoleName": "Greengrass_ServiceRole", "RoleId": "AROA5IPFWS7QFWCDRFRCP", "Arn": "arn:aws:iam::911551666144:role/Greengrass_ServiceRole", "CreateDate": "2024-10-03T06:58:24Z", "AssumeRolePolicyDocument": { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "Service": "greengrass.amazonaws.com" }, "Action": "sts:AssumeRole", "Condition": { "ArnLike": { "aws:SourceArn": "arn:aws:greengrass:region:account-id:*" }, "StringEquals": { "aws:SourceAccount": "account-id" } } } ] } } } ``` ``` "Arn": "arn:aws:iam::911551666144:role/Greengrass_ServiceRole" ``` * 建立完成此 IAM Role之後,將名為 AWSGreengrassResourceAccessRolePolicy 的IAM Policy掛載上去 ``` aws iam attach-role-policy --role-name Greengrass_ServiceRole --policy-arn arn:aws:iam::aws:policy/service-role/AWSGreengrassResourceAccessRolePolicy ``` * 最後要將 Greengrass_ServiceRole 與 Tokyo Region的 IoT Greengrass做一個綁定 ``` aws greengrassv2 associate-service-role-to-account --role-arn role-arn --region region ``` EX: ``` aws greengrassv2 associate-service-role-to-account --role-arn arn:aws:iam::911551666144:role/Greengrass_ServiceRole --region ap-northeast-1 ``` ``` ubuntu@ip-10-0-1-21:~$ aws greengrassv2 associate-service-role-to-account --role-arn arn:aws:iam::911551666144:role/Greengrass_ServiceRole --region ap-northeast-1 { "associatedAt": "2024-10-03T07:00:51Z" } ``` * 進到 AWS IoT Core console 也可以做一次驗證 * Step1: 進入 [Settings](https://ap-northeast-1.console.aws.amazon.com/iot/home?region=ap-northeast-1#/settings) 頁面 * Step2: 下拉到最底:Greengrass service role ![Screenshot 2024-10-03 at 3.02.48 PM](https://hackmd.io/_uploads/rkThZpjCC.png) ### 在 EC2 上安裝 JAVA [官方文件](https://docs.aws.amazon.com/corretto/latest/corretto-11-ug/generic-linux-install.html) * 執行 ``` wget -O - https://apt.corretto.aws/corretto.key | sudo gpg --dearmor -o /usr/share/keyrings/corretto-keyring.gpg && \ echo "deb [signed-by=/usr/share/keyrings/corretto-keyring.gpg] https://apt.corretto.aws stable main" | sudo tee /etc/apt/sources.list.d/corretto.list ``` * 輸出範例 ``` buntu@ip-10-0-1-21:~$ wget -O - https://apt.corretto.aws/corretto.key | sudo gpg --dearmor -o /usr/share/keyrings/corretto-keyring.gpg && \ echo "deb [signed-by=/usr/share/keyrings/corretto-keyring.gpg] https://apt.corretto.aws stable main" | sudo tee /etc/apt/sources.list.d/corretto.list --2024-10-03 06:30:58-- https://apt.corretto.aws/corretto.key Resolving apt.corretto.aws (apt.corretto.aws)... 3.164.110.69, 3.164.110.24, 3.164.110.83, ... Connecting to apt.corretto.aws (apt.corretto.aws)|3.164.110.69|:443... connected. HTTP request sent, awaiting response... 200 OK Length: 1695 (1.7K) [binary/octet-stream] Saving to: ‘STDOUT’ - 100%[=======================================================================================================>] 1.66K --.-KB/s in 0s 2024-10-03 06:30:58 (785 MB/s) - written to stdout [1695/1695] deb [signed-by=/usr/share/keyrings/corretto-keyring.gpg] https://apt.corretto.aws stable main ``` * 執行 ``` sudo apt-get update; sudo apt-get install -y java-11-amazon-corretto-jdk ``` * 一路 Enter 直到安裝完成 * 確認版本 ``` ubuntu@ip-10-0-1-21:~$ java -version openjdk version "11.0.24" 2024-07-16 LTS OpenJDK Runtime Environment Corretto-11.0.24.8.1 (build 11.0.24+8-LTS) OpenJDK 64-Bit Server VM Corretto-11.0.24.8.1 (build 11.0.24+8-LTS, mixed mode) ubuntu@ip-10-0-1-21:~$ ``` * 到此 Chapter 2 完成以下項目 2. 建立了 IAM User (workshop_user_delete_later) [權限一],例用 aksk 授權給 Greengrass Installer 安裝時所需要的 AWS 權限 3. 4. 建立了 AWS IoT Greengrass 的 Greengrass_ServiceRole,並且掛載了指定的 Policy,這是 Greengrass Cloud Service 後續再執行與設備端互動時,要求的 policy (AWSGreengrassResourceAccessRolePolicy)。並且完成與 AWS IoT Core的綁定 5. Edge Gateway (EC2) 環境準備,安裝 JAVA # Chapter 3 [Reference](https://catalog.workshops.aws/greengrass/en-US/chapter3-greengrasssetup) * 在 Chapter 2 我們準備好了安裝Greengrass的操作環境所需要的權限 * 在Chapter 3 當中,我們要準備 Greengrass 這個雲端服務,以及 Greengrass core device 這個設備端軟體,所需要的 IAM 權限。 ![image](https://hackmd.io/_uploads/ByCY72o00.png) ## Chapter 3.1 建立第一個 Greengrass Core Device * 在這個階段,我們要將一個Greengrass Core Device 安裝在一台 EC2 所模擬的 Edge Gateway上。 * 前往 AWS IoT Core > Greengrass devices > Core devices [console](https://ap-northeast-1.console.aws.amazon.com/iot/home?region=ap-northeast-1#/home),點擊橘色 **Set up one core device** ![Screenshot 2024-10-03 at 4.15.09 PM](https://hackmd.io/_uploads/r1C5f0sCC.png) * 保留預設值 * Step 1: Greengrass Core Device的名稱 * Step 2: 此Core Deivce要加入的 Thing Group * Step 3: 安裝 Greengrass Core Software ![Screenshot 2024-10-03 at 4.28.31 PM](https://hackmd.io/_uploads/HyZg8RiA0.png) ### Step 3: 安裝 Greengrass Core Software * Step 3.1 / 3.2 接已經完成 ![Screenshot 2024-10-03 at 4.45.21 PM](https://hackmd.io/_uploads/HJfntAj00.png) #### Step 3.3 執行 Greengrass Installer * 安裝 unzip ``` sudo apt install unzip ``` * 下載 Installer並解壓縮 ``` curl -s https://d2s8p88vqu9w66.cloudfront.net/releases/greengrass-nucleus-latest.zip > greengrass-nucleus-latest.zip && unzip greengrass-nucleus-latest.zip -d GreengrassInstaller ``` * 運行 Installer * 這邊的指令已經依據前面步驟對於Greengrass Code Device的設定帶入。是一段客製的指令 ``` sudo -E java -Droot="/greengrass/v2" -Dlog.store=FILE -jar ./GreengrassInstaller/lib/Greengrass.jar --aws-region ap-northeast-1 --thing-name GreengrassQuickStartCore-192517e784b --thing-group-name GreengrassQuickStartGroup --component-default-user ggc_user:ggc_group --provision true --setup-system-service true --deploy-dev-tools true ``` ![Screenshot 2024-10-03 at 4.45.42 PM](https://hackmd.io/_uploads/SJHyiAiCC.png) * 執行安裝指令 ``` sudo -E java -Droot="/greengrass/v2" -Dlog.store=FILE -jar ./GreengrassInstaller/lib/Greengrass.jar --aws-region ap-northeast-1 --thing-name GreengrassQuickStartCore-192517e784b --thing-group-name GreengrassQuickStartGroup --component-default-user ggc_user:ggc_group --provision true --setup-system-service true --deploy-dev-tools true ``` ##### 檢視安裝 Log * 建立 user / group ``` Creating user ggc_user ggc_user created Creating group ggc_group ggc_group created Added ggc_user to ggc_group ``` * 在Cloud side建立對應的 Thing 來代表這個裝置 ``` Provisioning AWS IoT resources for the device with IoT Thing Name: [GreengrassQuickStartCore-192517e784b]... Creating new IoT policy "GreengrassV2IoTThingPolicy" Creating keys and certificate... Attaching policy to certificate... Creating IoT Thing "GreengrassQuickStartCore-192517e784b"... Attaching certificate to IoT thing... Successfully provisioned AWS IoT resources for the device with IoT Thing Name: [GreengrassQuickStartCore-192517e784b]! ``` * 可以到 AWS IoT Core console 確認以上設定是否如實建立 https://ap-northeast-1.console.aws.amazon.com/iot/home?region=ap-northeast-1#/thinghub * 將這台設備的 Greengrass Core device 對應的 thing 加入到前面設定中指定的 thing group 當中,簡化未來針對機群的管理 ``` Adding IoT Thing [GreengrassQuickStartCore-192517e784b] into Thing Group: [GreengrassQuickStartGroup]... Successfully added Thing into Thing Group: [GreengrassQuickStartGroup] ``` * 設定 Greengrass Core device 裝置端所需要的權限 (GreengrassV2TokenExchangeRole) ``` Setting up resources for aws.greengrass.TokenExchangeService ... TES role alias "GreengrassV2TokenExchangeRoleAlias" does not exist, creating new alias... TES role "GreengrassV2TokenExchangeRole" does not exist, creating role... IoT role policy "GreengrassTESCertificatePolicyGreengrassV2TokenExchangeRoleAlias" for TES Role alias not exist, creating policy... Attaching TES role policy to IoT thing... No managed IAM policy found, looking for user defined policy... No IAM policy found, will attempt creating one... IAM role policy for TES "GreengrassV2TokenExchangeRoleAccess" created. This policy DOES NOT have S3 access, please modify it with your private components' artifact buckets/objects as needed when you create and deploy private components Attaching IAM role policy for TES to IAM role for TES... Configuring Nucleus with provisioned resource details... Downloading CA from "https://www.amazontrust.com/repository/AmazonRootCA1.pem" Created device configuration Successfully configured Nucleus with provisioned resource details! ``` * 進行預設的 Component Deployment ``` Creating a deployment for Greengrass first party components to the thing group Configured Nucleus to deploy aws.greengrass.Cli component Successfully set up Nucleus as a system service ``` * 至此已經完成 Greengrass Core device 的軟體安裝在EC2的流程。 * 在上面的log中可以看到一段log說明:IAM role policy for TES "GreengrassV2TokenExchangeRoleAccess" created. This policy DOES NOT have S3 access, please modify it with your private components' artifact buckets/objects as needed when you create and deploy private components ,因此我們需要再下一個流程中,按照需求加入component所需要的權限。 ## Chapter 3.2 加入 Component 所需權限 ### 建立 IAM Policy * 由於這次 workshop component 還需要以下權限 * Publish your custom component (using S3) * MQTT Bridge * 我們需要建立一個 IAM Policy,掛載到前面 Greengrass Core device 建立過程中,建立起來的 IAM Role,才能提供給 Greengrass Core device 端所需使用的 AWS 服務權限。 * 前往 AWS IAM Policy [console](https://us-east-1.console.aws.amazon.com/iam/home?region=ap-northeast-1#/policies) * 點擊橘色 Create policy ![Screenshot 2024-10-03 at 7.26.58 PM](https://hackmd.io/_uploads/SJokg-3RC.png) * 先將右側 Visual 切換至 JSON 模式 ![Screenshot 2024-10-03 at 7.27.07 PM](https://hackmd.io/_uploads/Syi1xWh0A.png) * 複製第四步之 IAM policy到 JSON Policy Editor區塊,點擊右下方橘色 Next ``` { "Version": "2012-10-17", "Statement": [ { "Sid": "S3BucketActions", "Effect": "Allow", "Action": [ "s3:CreateBucket", "s3:ListAllMyBuckets", "s3:GetBucketLocation", "s3:PutObject", "s3:GetObject" ], "Resource": [ "arn:aws:s3:::*" ] }, { "Effect": "Allow", "Action": [ "iot:*" ], "Resource": "*" }, { "Sid": "GreengrassActions", "Effect": "Allow", "Action": [ "greengrass:*" ], "Resource": "*" } ] } ``` ![Screenshot 2024-10-03 at 7.27.27 PM](https://hackmd.io/_uploads/H1sklZ30R.png) * 自行取名 Policy name,EX: workshop_s3_iot_gg_policy,並滑到右下方點擊橘色 Create policy ![Screenshot 2024-10-03 at 7.28.03 PM](https://hackmd.io/_uploads/rJjyg-hCR.png) * 搜尋 policy 列表,確認成功建立。 ![Screenshot 2024-10-03 at 7.28.22 PM](https://hackmd.io/_uploads/rkokg-hCC.png) * 前往 AWS IAM Role [console](https://us-east-1.console.aws.amazon.com/iam/home?region=ap-northeast-1#/roles),並在搜尋列搜尋 GreengrassV2TokenExchangeRole,此 Role 應該在前面 Greengrass installer 執行階段被建立,我們要將裡授予的權限,額外加入。 * 點擊 GreengrassV2TokenExchangeRole ![Screenshot 2024-10-03 at 7.35.10 PM](https://hackmd.io/_uploads/HyxK-b20C.png) * 在 Permissions 分頁,點擊 Add permissions 下拉選單,選擇 Attach policies。 ![Screenshot 2024-10-03 at 7.35.51 PM](https://hackmd.io/_uploads/BkTyG-nRC.png) * 搜尋 workshop_s3_iot_gg_policy ,勾選 workshop_s3_iot_gg_policy,點擊右側橘色 Add permissions ![Screenshot 2024-10-03 at 7.36.46 PM](https://hackmd.io/_uploads/Hka1G-n0R.png) * 完成後可以在 GreengrassV2TokenExchangeRole 的頁面看到 Permissions policies 有兩個附加 polcies ![Screenshot 2024-10-03 at 7.36.54 PM](https://hackmd.io/_uploads/rJ6yz-hAA.png) # Chapter 4 建立第一個 Component [Reference](https://catalog.workshops.aws/greengrass/en-US/chapter4-createfirstcomp) ## Chapter 4.0 利用 CLI 檢視目前設備上的狀態 * 確認 greengrass-cli 版本 ``` ubuntu@ip-10-0-1-21:~$ /greengrass/v2/bin/greengrass-cli -V Greengrass CLI Version: 2.13.0 ``` * 確認 systemctl 上的 greengrass.service 狀態 ``` ubuntu@ip-10-0-1-21:~$ sudo systemctl status greengrass.service ● greengrass.service - Greengrass Core Loaded: loaded (/etc/systemd/system/greengrass.service; enabled; vendor preset: enabled) Active: active (running) since Thu 2024-10-03 08:52:37 UTC; 2h 59min ago Main PID: 82324 (sh) Tasks: 45 (limit: 18962) Memory: 314.5M CPU: 27.693s CGroup: /system.slice/greengrass.service ├─82324 /bin/sh /greengrass/v2/alts/current/distro/bin/loader └─82332 java -Dlog.store=FILE -Dlog.store=FILE -Droot=/greengrass/v2 -jar /greengrass/v2/alts/current/distro/lib/Greengrass.jar --setup-system-service false Oct 03 08:52:37 ip-10-0-1-21 systemd[1]: Started Greengrass Core. Oct 03 08:52:37 ip-10-0-1-21 sh[82324]: Greengrass root: /greengrass/v2 Oct 03 08:52:37 ip-10-0-1-21 sh[82324]: Java executable: java Oct 03 08:52:37 ip-10-0-1-21 sh[82324]: JVM options: -Dlog.store=FILE -Droot=/greengrass/v2 Oct 03 08:52:37 ip-10-0-1-21 sh[82324]: Nucleus options: --setup-system-service false Oct 03 08:52:38 ip-10-0-1-21 sh[82332]: Launching Nucleus... Oct 03 08:52:39 ip-10-0-1-21 sh[82332]: Launched Nucleus successfully. ``` * 確認目前運行了哪些 Component? ``` ubuntu@ip-10-0-1-21:~$ sudo /greengrass/v2/bin/greengrass-cli component list Components currently running in Greengrass: Component Name: aws.greengrass.Nucleus Version: 2.13.0 State: FINISHED Configuration: {"awsRegion":"ap-northeast-1","componentStoreMaxSizeBytes":"10000000000","deploymentPollingFrequencySeconds":"15","envStage":"prod","fipsMode":"false","fleetStatus":{"periodicStatusPublishIntervalSeconds":86400.0},"greengrassDataPlaneEndpoint":"","greengrassDataPlanePort":"8443","httpClient":{},"iotCredEndpoint":"c2s1jcn8qal0l7.credentials.iot.ap-northeast-1.amazonaws.com","iotDataEndpoint":"a1ae47nl46extm-ats.iot.ap-northeast-1.amazonaws.com","iotRoleAlias":"GreengrassV2TokenExchangeRoleAlias","jvmOptions":"-Dlog.store=FILE","logging":{},"mqtt":{"spooler":{}},"networkProxy":{"proxy":{}},"platformOverride":{},"runWithDefault":{"posixUser":"ggc_user:ggc_group"},"s3EndpointType":"GLOBAL","telemetry":{}} Component Name: UpdateSystemPolicyService Version: 0.0.0 State: RUNNING Configuration: null Component Name: FleetStatusService Version: null State: RUNNING Configuration: null Component Name: DeploymentService Version: 0.0.0 State: RUNNING Configuration: null Component Name: TelemetryAgent Version: 0.0.0 State: RUNNING Configuration: null Component Name: aws.greengrass.Cli Version: 2.13.0 State: RUNNING Configuration: {"AuthorizedPosixGroups":null,"AuthorizedWindowsGroups":null} ubuntu@ip-10-0-1-21:~$ ``` ## Chapter 4.1 Creating https://catalog.workshops.aws/greengrass/en-US/chapter4-createfirstcomp/10-step1 [官方文件](https://docs.aws.amazon.com/greengrass/v2/developerguide/create-components.html) ### 建立一個Helloworld componet ![image](https://hackmd.io/_uploads/rJAj6-nCC.png) * 建立 artifact 檔案,貼上程式碼 https://catalog.workshops.aws/greengrass/en-US/chapter4-createfirstcomp/10-step1#creating-component-artifacts * 建立 artifacts 的檔案結構 ``` mkdir -p ~/environment/GreengrassCore/artifacts/com.example.HelloWorld/1.0.0 && touch ~/environment/GreengrassCore/artifacts/com.example.HelloWorld/1.0.0/hello_world.py ``` * 範例程式碼 hello_world.py ``` # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. # SPDX-License-Identifier: MIT-0 import sys import datetime import time while True: message = f"Hello, {sys.argv[1]}! Current time: {str(datetime.datetime.now())}." # Print the message to stdout. print(message) # Append the message to the log file. with open('/tmp/Greengrass_HelloWorld.log', 'a') as f: print(message, file=f) time.sleep(1) ``` ![Screenshot 2024-10-03 at 8.29.06 PM](https://hackmd.io/_uploads/H1EtAZ3RA.png) * 建立 recipe 檔案,貼上程式碼 https://catalog.workshops.aws/greengrass/en-US/chapter4-createfirstcomp/10-step1#creating-a-recipe-for-a-component * 建立 recipes 的檔案結構 ``` mkdir -p ~/environment/GreengrassCore/recipes && touch ~/environment/GreengrassCore/recipes/com.example.HelloWorld-1.0.0.json ``` * 範例程式碼 com.example.HelloWorld-1.0.0.json ``` { "RecipeFormatVersion": "2020-01-25", "ComponentName": "com.example.HelloWorld", "ComponentVersion": "1.0.0", "ComponentDescription": "My first AWS IoT Greengrass component.", "ComponentPublisher": "Amazon", "ComponentConfiguration": { "DefaultConfiguration": { "Message": "world" } }, "Manifests": [ { "Platform": { "os": "linux" }, "Lifecycle": { "Run": "python3 -u {artifacts:path}/hello_world.py '{configuration:/Message}'\n" } } ] } ``` ## Chapter 4.2 Run and test https://catalog.workshops.aws/greengrass/en-US/chapter4-createfirstcomp/20-step2 * 該怎麼印出 Greengrass log 來了解他的運作狀況 ``` sudo tail -F /greengrass/v2/logs/greengrass.log ``` * 接下來我們要將上面的Component Hello World 部署到greengrass上,我們會利用 greengrass cli在device上執行部署工作 ``` sudo /greengrass/v2/bin/greengrass-cli deployment create \ --recipeDir ~/environment/GreengrassCore/recipes \ --artifactDir ~/environment/GreengrassCore/artifacts \ --merge "com.example.HelloWorld=1.0.0" ``` * 執行結果 ``` ubuntu@ip-10-0-1-21:~$ sudo /greengrass/v2/bin/greengrass-cli deployment create \ --recipeDir ~/environment/GreengrassCore/recipes \ --artifactDir ~/environment/GreengrassCore/artifacts \ --merge "com.example.HelloWorld=1.0.0" Local deployment submitted! Deployment Id: f7e8374a-42f4-48ed-94ff-2f61052762ab ``` * 根據程式碼,我們知道Hello world會對 /tmp/Greengrass_HelloWorld.log 持續寫入 ``` ubuntu@ip-10-0-1-21:~$ tail -F /tmp/Greengrass_HelloWorld.log Hello, world! Current time: 2024-10-03 12:44:26.965872. Hello, world! Current time: 2024-10-03 12:44:27.967104. Hello, world! Current time: 2024-10-03 12:44:28.968408. Hello, world! Current time: 2024-10-03 12:44:29.969654. Hello, world! Current time: 2024-10-03 12:44:30.970868. Hello, world! Current time: 2024-10-03 12:44:31.972124. Hello, world! Current time: 2024-10-03 12:44:32.973385. Hello, world! Current time: 2024-10-03 12:44:33.974689. Hello, world! Current time: 2024-10-03 12:44:34.975927. Hello, world! Current time: 2024-10-03 12:44:35.977168. Hello, world! Current time: 2024-10-03 12:44:36.978405. Hello, world! Current time: 2024-10-03 12:44:37.979632. ``` * 此時再度 list component ``` ubuntu@ip-10-0-1-21:~$ sudo /greengrass/v2/bin/greengrass-cli component list Components currently running in Greengrass: Component Name: aws.greengrass.Nucleus Version: 2.13.0 State: FINISHED Configuration: {"awsRegion":"ap-northeast-1","componentStoreMaxSizeBytes":"10000000000","deploymentPollingFrequencySeconds":"15","envStage":"prod","fipsMode":"false","fleetStatus":{"periodicStatusPublishIntervalSeconds":86400.0},"greengrassDataPlaneEndpoint":"","greengrassDataPlanePort":"8443","httpClient":{},"iotCredEndpoint":"c2s1jcn8qal0l7.credentials.iot.ap-northeast-1.amazonaws.com","iotDataEndpoint":"a1ae47nl46extm-ats.iot.ap-northeast-1.amazonaws.com","iotRoleAlias":"GreengrassV2TokenExchangeRoleAlias","jvmOptions":"-Dlog.store=FILE","logging":{},"mqtt":{"spooler":{}},"networkProxy":{"proxy":{}},"platformOverride":{},"runWithDefault":{"posixShell":"sh","posixUser":"ggc_user:ggc_group"},"s3EndpointType":"GLOBAL","telemetry":{}} Component Name: com.example.HelloWorld Version: 1.0.0 State: RUNNING Configuration: {"Message":"world"} Component Name: UpdateSystemPolicyService Version: 0.0.0 State: RUNNING Configuration: null Component Name: FleetStatusService Version: null State: RUNNING Configuration: null Component Name: DeploymentService Version: 0.0.0 State: RUNNING Configuration: null Component Name: TelemetryAgent Version: 0.0.0 State: RUNNING Configuration: null Component Name: aws.greengrass.Cli Version: 2.13.0 State: RUNNING Configuration: {"AuthorizedPosixGroups":null,"AuthorizedWindowsGroups":null} ``` ## Chapter 4.3 Publish https://catalog.workshops.aws/greengrass/en-US/chapter4-createfirstcomp/30-step3 ### 建立 S3 bucket 來儲存 artifacts 與 recipe * 執行 ``` EPOCH_TIME=$(date +"%s") && S3_BUCKET=ggcv2-workshop-$HOSTNAME-$EPOCH_TIME && aws s3 mb s3://$S3_BUCKET --region $AWS_DEFAULT_REGION ``` * 結果 ``` ubuntu@ip-10-0-1-21:~$ EPOCH_TIME=$(date +"%s") && S3_BUCKET=ggcv2-workshop-$HOSTNAME-$EPOCH_TIME && aws s3 mb s3://$S3_BUCKET --region $AWS_DEFAULT_REGION make_bucket: ggcv2-workshop-ip-10-0-1-21-1727960283 ``` * 修改 com.example.HelloWorld-1.0.0.json * '[YOUR BUCKET NAME]': 剛剛建立的:EX: ggcv2-workshop-ip-10-0-1-21-1727960283 * 指定好 Component 的 Artifact 路徑在 recipe 之後,我們就準備將檔案上傳到指定的 S3 ``` { "RecipeFormatVersion": "2020-01-25", "ComponentName": "com.example.HelloWorld", "ComponentVersion": "1.0.0", "ComponentDescription": "My first AWS IoT Greengrass component.", "ComponentPublisher": "Amazon", "ComponentConfiguration": { "DefaultConfiguration": { "Message": "world" } }, "Manifests": [ { "Platform": { "os": "linux" }, "Lifecycle": { "Run": "python3 -u {artifacts:path}/hello_world.py '{configuration:/Message}'\n" }, "Artifacts": [ { "URI": "s3://[YOUR BUCKET NAME]/artifacts/com.example.HelloWorld/1.0.0/hello_world.py" } ] } ] } ``` * 執行 ``` aws s3 cp --recursive /home/ubuntu/environment/GreengrassCore/ s3://$S3_BUCKET/ ``` * 結果 ``` ubuntu@ip-10-0-1-21:~$ aws s3 cp --recursive /home/ubuntu/environment/GreengrassCore/ s3://$S3_BUCKET/ upload: environment/GreengrassCore/recipes/com.example.HelloWorld-1.0.0.json to s3://ggcv2-workshop-ip-10-0-1-21-1727960283/recipes/com.example.HelloWorld-1.0.0.json upload: environment/GreengrassCore/artifacts/com.example.HelloWorld/1.0.0/hello_world.py to s3://ggcv2-workshop-ip-10-0-1-21-1727960283/artifacts/com.example.HelloWorld/1.0.0/hello_world.py ``` * 可以練習到 AWS S3 Console 找找看是否正確上傳 ![Screenshot 2024-10-03 at 8.30.27 PM](https://hackmd.io/_uploads/Byk50bhA0.png) ![Screenshot 2024-09-29 at 12.11.15 AM](https://hackmd.io/_uploads/Hy4hunc0R.png) ![Screenshot 2024-09-29 at 12.11.28 AM](https://hackmd.io/_uploads/BJEhd2qAC.png) * 接下來就要正式將recipe註冊到 AWS Greengrass component ``` cd /home/ubuntu/environment/GreengrassCore/recipes && aws greengrassv2 create-component-version --inline-recipe fileb://com.example.HelloWorld-1.0.0.json --region $AWS_DEFAULT_REGION ``` * 執行結果 ``` ubuntu@ip-10-0-1-21:~$ cd /home/ubuntu/environment/GreengrassCore/recipes && aws greengrassv2 create-component-version --inline-recipe fileb://com.example.HelloWorld-1.0.0.json --region $AWS_DEFAULT_REGION { "arn": "arn:aws:greengrass:ap-northeast-1:911551666144:components:com.example.HelloWorld:versions:1.0.0", "componentName": "com.example.HelloWorld", "componentVersion": "1.0.0", "creationTimestamp": 1727961345.645, "status": { "componentState": "REQUESTED", "message": "NONE", "errors": {} } } ``` * 執行以下指令,列出目前 Greengrass 裡自建的component ``` ubuntu@ip-10-0-1-21:~/environment/GreengrassCore/recipes$ aws greengrassv2 list-components --region $AWS_DEFAULT_REGION { "components": [ { "arn": "arn:aws:greengrass:ap-northeast-1:911551666144:components:com.example.HelloWorld", "componentName": "com.example.HelloWorld", "latestVersion": { "arn": "arn:aws:greengrass:ap-northeast-1:911551666144:components:com.example.HelloWorld:versions:1.0.0", "componentVersion": "1.0.0", "creationTimestamp": 1727961345.645, "description": "My first AWS IoT Greengrass component.", "publisher": "Amazon", "platforms": [ { "attributes": { "os": "linux" } } ] } } ] } ``` * AWS Greengrass console 中也可以看見此 component ![Screenshot 2024-09-29 at 12.27.39 AM](https://hackmd.io/_uploads/BkgN3d2q00.png) ## Chapter 4.4 Deploy https://catalog.workshops.aws/greengrass/en-US/chapter4-createfirstcomp/40-step4 * 既然已經publish,就可以正式利用 AWS Console,將指定的 Component 部署到指定的 Greengrass Core device上。 * 移除第一次的 Local Deployment ``` sudo /greengrass/v2/bin/greengrass-cli deployment create --remove "com.example.HelloWorld" ``` * 確認已經移除 ``` ubuntu@ip-10-0-1-21:~/environment/GreengrassCore/recipes$ sudo /greengrass/v2/bin/greengrass-cli deployment create --remove "com.example.HelloWorld" Local deployment submitted! Deployment Id: 4022ff29-a208-49d5-ac43-ce3b8e67363d ubuntu@ip-10-0-1-21:~/environment/GreengrassCore/recipes$ sudo /greengrass/v2/bin/greengrass-cli component list Components currently running in Greengrass: Component Name: aws.greengrass.Nucleus Version: 2.13.0 State: FINISHED Configuration: {"awsRegion":"ap-northeast-1","componentStoreMaxSizeBytes":"10000000000","deploymentPollingFrequencySeconds":"15","envStage":"prod","fipsMode":"false","fleetStatus":{"periodicStatusPublishIntervalSeconds":86400.0},"greengrassDataPlaneEndpoint":"","greengrassDataPlanePort":"8443","httpClient":{},"iotCredEndpoint":"c2s1jcn8qal0l7.credentials.iot.ap-northeast-1.amazonaws.com","iotDataEndpoint":"a1ae47nl46extm-ats.iot.ap-northeast-1.amazonaws.com","iotRoleAlias":"GreengrassV2TokenExchangeRoleAlias","jvmOptions":"-Dlog.store=FILE","logging":{},"mqtt":{"spooler":{}},"networkProxy":{"proxy":{}},"platformOverride":{},"runWithDefault":{"posixShell":"sh","posixUser":"ggc_user:ggc_group"},"s3EndpointType":"GLOBAL","telemetry":{}} Component Name: UpdateSystemPolicyService Version: 0.0.0 State: RUNNING Configuration: null Component Name: FleetStatusService Version: null State: RUNNING Configuration: null Component Name: DeploymentService Version: 0.0.0 State: RUNNING Configuration: null Component Name: TelemetryAgent Version: 0.0.0 State: RUNNING Configuration: null Component Name: aws.greengrass.Cli Version: 2.13.0 State: RUNNING Configuration: {"AuthorizedPosixGroups":null,"AuthorizedWindowsGroups":null} ``` * 前往 AWS IoT Greengrass Deployment [console](https://ap-northeast-1.console.aws.amazon.com/iot/home?region=ap-northeast-1#/greengrass/v2/deployments) * 勾選 Deployment for GreengrassQuickStartGroup,接著點擊 Revise ![Screenshot 2024-10-03 at 9.21.57 PM](https://hackmd.io/_uploads/rkiicM2CC.png) * Step 1 - Specify target 維持預設,我們還是針對我們這一台Greengrass Core device 進行 Deploy,點擊右下橘色 Next * Step 2 - Select components,選取 com.example.HelloWorld,點擊右下橘色 Next ![Screenshot 2024-10-03 at 9.24.29 PM](https://hackmd.io/_uploads/rysmoGnCR.png) * Step 3 - Configure 選中的components,勾選後點擊Configure component ![Screenshot 2024-10-03 at 9.43.24 PM](https://hackmd.io/_uploads/ByN5yQ30C.png) * 在 Configure to merge 內加上 Message ``` { "Message": "this was deployed from AWS IoT Core" } ``` * 點擊 Confirm ![Screenshot 2024-10-03 at 9.44.40 PM](https://hackmd.io/_uploads/B1IxlQnAR.png) * 接著點擊右下角橘色 Next * Step 4 - Configure advanced settings,保持預設,點擊右下角橘色 Next ![Screenshot 2024-10-03 at 9.46.48 PM](https://hackmd.io/_uploads/H1V_e7hAC.png) * Step 5 - Review,確認無誤後,點擊最底下橘色 Deploy,接這可以開始監控 Execution 狀況 ![Screenshot 2024-10-03 at 9.48.53 PM](https://hackmd.io/_uploads/S180eQnCR.png) * 藉由 Execution overview dashboard 可以監控部署狀況 ![Screenshot 2024-10-03 at 9.49.10 PM](https://hackmd.io/_uploads/r1F-bm2RA.png) ![Screenshot 2024-10-03 at 9.49.17 PM](https://hackmd.io/_uploads/S1t-bX20A.png) * 當看到Succeeded之後,可以回到 VSC Terminal,列出元件 ``` ubuntu@ip-10-0-1-21:~/environment/GreengrassCore/recipes$ sudo /greengrass/v2/bin/greengrass-cli component list Components currently running in Greengrass: Component Name: aws.greengrass.Nucleus Version: 2.13.0 State: FINISHED Configuration: {"awsRegion":"ap-northeast-1","componentStoreMaxSizeBytes":"10000000000","deploymentPollingFrequencySeconds":"15","envStage":"prod","fipsMode":"false","fleetStatus":{"periodicStatusPublishIntervalSeconds":86400.0},"greengrassDataPlaneEndpoint":"","greengrassDataPlanePort":"8443","httpClient":{},"iotCredEndpoint":"c2s1jcn8qal0l7.credentials.iot.ap-northeast-1.amazonaws.com","iotDataEndpoint":"a1ae47nl46extm-ats.iot.ap-northeast-1.amazonaws.com","iotRoleAlias":"GreengrassV2TokenExchangeRoleAlias","jvmOptions":"-Dlog.store=FILE","logging":{},"mqtt":{"spooler":{}},"networkProxy":{"proxy":{}},"platformOverride":{},"runWithDefault":{"posixShell":"sh","posixUser":"ggc_user:ggc_group"},"s3EndpointType":"GLOBAL","telemetry":{}} Component Name: UpdateSystemPolicyService Version: 0.0.0 State: RUNNING Configuration: null Component Name: FleetStatusService Version: null State: RUNNING Configuration: null Component Name: com.example.HelloWorld Version: 1.0.0 State: RUNNING Configuration: {"Message":"this was deployed from AWS IoT Core"} Component Name: DeploymentService Version: 0.0.0 State: RUNNING Configuration: null Component Name: TelemetryAgent Version: 0.0.0 State: RUNNING Configuration: null Component Name: aws.greengrass.Cli Version: 2.13.0 State: RUNNING Configuration: {"AuthorizedPosixGroups":null,"AuthorizedWindowsGroups":null} ``` * 可以看到剛剛 Config的 message ``` Component Name: com.example.HelloWorld Version: 1.0.0 State: RUNNING Configuration: {"Message":"this was deployed from AWS IoT Core"} ``` * 而目前 component 輸出的log也改為 ``` ubuntu@ip-10-0-1-21:~$ tail -F /tmp/Greengrass_HelloWorld.log Hello, this was deployed from AWS IoT Core! Current time: 2024-10-03 14:02:43.066374. Hello, this was deployed from AWS IoT Core! Current time: 2024-10-03 14:02:44.067647. Hello, this was deployed from AWS IoT Core! Current time: 2024-10-03 14:02:45.068968. Hello, this was deployed from AWS IoT Core! Current time: 2024-10-03 14:02:46.070228. Hello, this was deployed from AWS IoT Core! Current time: 2024-10-03 14:02:47.071518. Hello, this was deployed from AWS IoT Core! Current time: 2024-10-03 14:02:48.072745. ``` ## Chapter 4.5 Update https://catalog.workshops.aws/greengrass/en-US/chapter4-createfirstcomp/40-step5 * 接下來我們要練習更新版本, ### 建立新版本的 artifact 以及 recipe * 執行建立新的artifact檔案 ``` mkdir -p ~/environment/GreengrassCore/artifacts/com.example.HelloWorld/1.1.0 && touch ~/environment/GreengrassCore/artifacts/com.example.HelloWorld/1.1.0/second_iteration.py ``` * second_iteraton.py ``` # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. # SPDX-License-Identifier: MIT-0 import sys import datetime import time while True: message = f"Hello, {sys.argv[1]}! This is the second iteration. Current time: {str(datetime.datetime.now())}." # Print the message to stdout. print(message) # Append the message to the log file. with open('/tmp/Greengrass_SecondIteration.log', 'a') as f: print(message, file=f) time.sleep(1) ``` * 建立新的 recipe:com.example.HelloWorld-1.1.0.json在/home/ubuntu/environment/GreengrassCore/recipes * 記得更換 [YOUR BUCKET NAME]: EX:ggcv2-workshop-ip-10-0-1-21-1727960283 ``` { "RecipeFormatVersion": "2020-01-25", "ComponentName": "com.example.HelloWorld", "ComponentVersion": "1.1.0", "ComponentDescription": "My first AWS IoT Greengrass component.", "ComponentPublisher": "Amazon", "ComponentConfiguration": { "DefaultConfiguration": { "Message": "world" } }, "Manifests": [ { "Platform": { "os": "linux" }, "Lifecycle": { "Run": "python3 -u {artifacts:path}/second_iteration.py '{configuration:/Message}'\n" }, "Artifacts": [ { "URI": "s3://[YOUR BUCKET NAME]/artifacts/com.example.HelloWorld/1.1.0/second_iteration.py" } ] } ] } ``` ### 上完更新到 S3 * 執行 ``` aws s3 cp --recursive ~/environment/GreengrassCore/ s3://$S3_BUCKET/ ``` * 結果 ``` ubuntu@ip-10-0-1-21:~/environment/GreengrassCore/recipes$ aws s3 cp --recursive ~/environment/GreengrassCore/ s3://$S3_BUCKET/ upload: ./com.example.HelloWorld-1.0.0.json to s3://ggcv2-workshop-ip-10-0-1-21-1727960283/recipes/com.example.HelloWorld-1.0.0.json upload: ../artifacts/com.example.HelloWorld/1.0.0/hello_world.py to s3://ggcv2-workshop-ip-10-0-1-21-1727960283/artifacts/com.example.HelloWorld/1.0.0/hello_world.py upload: ../artifacts/com.example.HelloWorld/1.1.0/second_iteration.py to s3://ggcv2-workshop-ip-10-0-1-21-1727960283/artifacts/com.example.HelloWorld/1.1.0/second_iteration.py upload: ./com.example.HelloWorld-1.1.0.json to s3://ggcv2-workshop-ip-10-0-1-21-1727960283/recipes/com.example.HelloWorld-1.1.0.json ``` ### Publish 新版component * 執行 ``` cd ~/environment/GreengrassCore/recipes && aws greengrassv2 create-component-version --inline-recipe fileb://com.example.HelloWorld-1.1.0.json --region $AWS_DEFAULT_REGION ``` * 結果 ``` ubuntu@ip-10-0-1-21:~/environment/GreengrassCore/recipes$ cd ~/environment/GreengrassCore/recipes && aws greengrassv2 create-component-version --inline-recipe fileb://com.example.HelloWorld-1.1.0.json --region $AWS_DEFAULT_REGION { "arn": "arn:aws:greengrass:ap-northeast-1:911551666144:components:com.example.HelloWorld:versions:1.1.0", "componentName": "com.example.HelloWorld", "componentVersion": "1.1.0", "creationTimestamp": 1727964925.121, "status": { "componentState": "REQUESTED", "message": "NONE", "errors": {} } } ``` ### 重複 4.4 的步驟進行更新 * 將 component revise 到 version 1.1.0,點擊 confirm ![Screenshot 2024-10-03 at 10.17.53 PM](https://hackmd.io/_uploads/rJdCP73RC.png) * 最後同樣點擊 Deploy * Execution * queued * In progress * Succeeded * 部署成功後,確認日誌 ``` ubuntu@ip-10-0-1-21:~$ tail -F /tmp/Greengrass_SecondIteration.log Hello, this was deployed from AWS IoT Core! This is the second iteration. Current time: 2024-10-03 14:20:27.199849. Hello, this was deployed from AWS IoT Core! This is the second iteration. Current time: 2024-10-03 14:20:28.201047. Hello, this was deployed from AWS IoT Core! This is the second iteration. Current time: 2024-10-03 14:20:29.202238. Hello, this was deployed from AWS IoT Core! This is the second iteration. Current time: 2024-10-03 14:20:30.203451. Hello, this was deployed from AWS IoT Core! This is the second iteration. Current time: 2024-10-03 14:20:31.204732. ``` # Chapter 5 Pub / Sub IPC [Reference](https://catalog.workshops.aws/greengrass/en-US/chapter5-pubsubipc) ![image](https://hackmd.io/_uploads/HyYOOQ20R.png) * Greengrass 2.0 provides an inter-process communication (IPC) SDK for inter-process communication. By using IPC SDK feature, Publisher (Pub) and Subscriber (Sub) communication can be performed between each process of the custom component. * In this step, you'll learn how to create a component that reads sensor data and keeps publishing to a topic, a component that subscribes to that topic logs and receives the messages, and a custom component that uses IPC. ## Chapter 5.1 Publisher * 安裝 pip3 ``` sudo apt install python3-pip ``` * 建立 artifact ``` mkdir -p ~/environment/GreengrassCore/artifacts/com.example.Publisher/1.0.0 && cd ~/environment/GreengrassCore/artifacts/com.example.Publisher/1.0.0 touch example_publisher.py dummy_sensor.py ``` * 複製以下程式碼到 dummy_sensor.py ``` # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. # SPDX-License-Identifier: MIT-0 from random import gauss class DummySensor(object): def __init__(self, mean=25, variance=1): self.mu = mean self.sigma = variance def read_value(self): return float("%.2f" % (gauss(1000, 20))) if __name__ == '__main__': sensor = DummySensor() print(sensor.read_value()) ``` * 複製以下程式碼到 example_publisher.py ``` # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. # SPDX-License-Identifier: MIT-0 import time import datetime import json import awsiot.greengrasscoreipc from awsiot.greengrasscoreipc.model import ( PublishToTopicRequest, PublishMessage, JsonMessage ) from dummy_sensor import DummySensor TIMEOUT = 10 publish_rate = 1.0 ipc_client = awsiot.greengrasscoreipc.connect() sensor = DummySensor() topic = "my/topic" while True: message = {"timestamp": str(datetime.datetime.now()), "value": sensor.read_value()} message_json = json.dumps(message).encode('utf-8') request = PublishToTopicRequest() request.topic = topic publish_message = PublishMessage() publish_message.json_message = JsonMessage() publish_message.json_message.message = message request.publish_message = publish_message operation = ipc_client.new_publish_to_topic() operation.activate(request) future = operation.get_response() future.result(TIMEOUT) print("publish") time.sleep(1/publish_rate) ``` * 建立 recipe: com.example.Publisher-1.0.0.json ``` touch ~/environment/GreengrassCore/recipes/com.example.Publisher-1.0.0.json ``` ![Screenshot 2024-10-03 at 10.29.47 PM](https://hackmd.io/_uploads/Hy2PcmnRC.png) * 複製程式碼到 com.example.Publisher-1.0.0.json ``` { "RecipeFormatVersion": "2020-01-25", "ComponentName": "com.example.Publisher", "ComponentVersion": "1.0.0", "ComponentDescription": "A component that publishes messages.", "ComponentPublisher": "Amazon", "ComponentConfiguration": { "DefaultConfiguration": { "accessControl": { "aws.greengrass.ipc.pubsub": { "com.example.Publisher:pubsub:1": { "policyDescription": "Allows access to publish to all topics.", "operations": [ "aws.greengrass#PublishToTopic" ], "resources": [ "*" ] } } } } }, "Manifests": [ { "Lifecycle": { "Install": "pip3 install awsiotsdk numpy", "Run": "python3 -u {artifacts:path}/example_publisher.py" } } ] } ``` * 執行:Local Deploy Publisher ``` sudo /greengrass/v2/bin/greengrass-cli deployment create \ --recipeDir ~/environment/GreengrassCore/recipes \ --artifactDir ~/environment/GreengrassCore/artifacts \ --merge "com.example.Publisher=1.0.0" ``` * 結果 ``` ubuntu@ip-10-0-1-21:~/environment/GreengrassCore/artifacts/com.example.Publisher/1.0.0$ sudo /greengrass/v2/bin/greengrass-cli deployment create \ --recipeDir ~/environment/GreengrassCore/recipes \ --artifactDir ~/environment/GreengrassCore/artifacts \ --merge "com.example.Publisher=1.0.0" Local deployment submitted! Deployment Id: 2cf87f37-e0b9-4d4b-9995-ef6e2e2c5986 ``` ## Chapter 5.2 Subscriber * 接下來這邊要建立一個Subscriber去訂閱上一段建立的topic * 建立 Subsriber component ``` mkdir -p ~/environment/GreengrassCore/artifacts/com.example.Subscriber/1.0.0 && cd ~/environment/GreengrassCore/artifacts/com.example.Subscriber/1.0.0 touch example_subscriber.py ``` * 複製程式碼到 example_subscriber.py ``` # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. # SPDX-License-Identifier: MIT-0 import time import json import awsiot.greengrasscoreipc import awsiot.greengrasscoreipc.client as client from awsiot.greengrasscoreipc.model import ( SubscribeToTopicRequest, SubscriptionResponseMessage ) TIMEOUT = 10 ipc_client = awsiot.greengrasscoreipc.connect() class StreamHandler(client.SubscribeToTopicStreamHandler): def __init__(self): super().__init__() def on_stream_event(self, event: SubscriptionResponseMessage) -> None: message_string = event.json_message.message with open('/tmp/Greengrass_Subscriber.log', 'a') as f: print(message_string, file=f) def on_stream_error(self, error: Exception) -> bool: return True def on_stream_closed(self) -> None: pass topic = "my/topic" request = SubscribeToTopicRequest() request.topic = topic handler = StreamHandler() operation = ipc_client.new_subscribe_to_topic(handler) future = operation.activate(request) while True: time.sleep(1) operation.close() ``` * 建立 recipe ``` touch ~/environment/GreengrassCore/recipes/com.example.Subscriber-1.0.0.json ``` * 複製組態到 com.example.Subscriber-1.0.0.json ``` { "RecipeFormatVersion": "2020-01-25", "ComponentName": "com.example.Subscriber", "ComponentVersion": "1.0.0", "ComponentDescription": "A component that subscribes to messages.", "ComponentPublisher": "Amazon", "ComponentConfiguration": { "DefaultConfiguration": { "accessControl": { "aws.greengrass.ipc.pubsub": { "com.example.Subscriber:pubsub:1": { "policyDescription": "Allows access to publish to all topics.", "operations": [ "aws.greengrass#SubscribeToTopic" ], "resources": [ "*" ] } } } } }, "Manifests": [ { "Lifecycle": { "Install": "pip3 install awsiotsdk", "Run": "python3 -u {artifacts:path}/example_subscriber.py" } } ] } ``` * 執行:Local Deploy component ``` sudo /greengrass/v2/bin/greengrass-cli deployment create \ --recipeDir ~/environment/GreengrassCore/recipes \ --artifactDir ~/environment/GreengrassCore/artifacts \ --merge "com.example.Subscriber=1.0.0" ``` * 結果 ``` ubuntu@ip-10-0-1-21:~/environment/GreengrassCore/artifacts/com.example.Subscriber/1.0.0$ sudo /greengrass/v2/bin/greengrass-cli deployment create \ --recipeDir ~/environment/GreengrassCore/recipes \ --artifactDir ~/environment/GreengrassCore/artifacts \ --merge "com.example.Subscriber=1.0.0" Local deployment submitted! Deployment Id: 27182195-27e2-4726-888a-d8f5795d1a13 ``` * list component,確認 publisher / subscriber 皆為 running 狀態 ``` ubuntu@ip-10-0-1-21:~/environment/GreengrassCore/artifacts/com.example.Subscriber/1.0.0$ sudo /greengrass/v2/bin/greengrass-cli component list Components currently running in Greengrass: Component Name: FleetStatusService Version: 0.0.0 State: RUNNING Configuration: null Component Name: aws.greengrass.Nucleus Version: 2.13.0 State: FINISHED Configuration: {"awsRegion":"ap-northeast-1","componentStoreMaxSizeBytes":"10000000000","deploymentPollingFrequencySeconds":"15","envStage":"prod","fipsMode":"false","fleetStatus":{"periodicStatusPublishIntervalSeconds":86400.0},"greengrassDataPlaneEndpoint":"","greengrassDataPlanePort":"8443","httpClient":{},"iotCredEndpoint":"c2s1jcn8qal0l7.credentials.iot.ap-northeast-1.amazonaws.com","iotDataEndpoint":"a1ae47nl46extm-ats.iot.ap-northeast-1.amazonaws.com","iotRoleAlias":"GreengrassV2TokenExchangeRoleAlias","jvmOptions":"-Dlog.store=FILE","logging":{},"mqtt":{"spooler":{}},"networkProxy":{"proxy":{}},"platformOverride":{},"runWithDefault":{"posixShell":"sh","posixUser":"ggc_user:ggc_group"},"s3EndpointType":"GLOBAL","telemetry":{}} Component Name: TelemetryAgent Version: 0.0.0 State: RUNNING Configuration: null Component Name: DeploymentService Version: 0.0.0 State: RUNNING Configuration: null Component Name: com.example.HelloWorld Version: 1.1.0 State: RUNNING Configuration: {"Message":"this was deployed from AWS IoT Core"} Component Name: com.example.Subscriber Version: 1.0.0 State: RUNNING Configuration: {"accessControl":{"aws.greengrass.ipc.pubsub":{"com.example.Subscriber:pubsub:1":{"operations":["aws.greengrass#SubscribeToTopic"],"policyDescription":"Allows access to publish to all topics.","resources":["*"]}}}} Component Name: UpdateSystemPolicyService Version: 0.0.0 State: RUNNING Configuration: null Component Name: aws.greengrass.Cli Version: 2.13.0 State: RUNNING Configuration: {"AuthorizedPosixGroups":null,"AuthorizedWindowsGroups":null} ``` * 確認 Subsriber log ``` ubuntu@ip-10-0-1-21:~$ tail -f /tmp/Greengrass_Subscriber.log {'timestamp': '2024-10-03 15:49:11.648591', 'value': 970.55} {'timestamp': '2024-10-03 15:49:12.650617', 'value': 1008.2} {'timestamp': '2024-10-03 15:49:13.651715', 'value': 978.8} {'timestamp': '2024-10-03 15:49:14.653623', 'value': 1008.35} {'timestamp': '2024-10-03 15:49:15.655601', 'value': 1022.48} {'timestamp': '2024-10-03 15:49:16.657626', 'value': 1034.62} {'timestamp': '2024-10-03 15:49:17.659607', 'value': 1001.03} {'timestamp': '2024-10-03 15:49:18.661541', 'value': 1003.84} {'timestamp': '2024-10-03 15:49:19.663618', 'value': 979.3} {'timestamp': '2024-10-03 15:49:20.665725', 'value': 971.69} {'timestamp': '2024-10-03 15:49:21.667684', 'value': 1005.52} ``` # Chapter 7 Containers with Greengrass [Reference](https://catalog.workshops.aws/greengrass/en-US/chapter7-containers) ## Chapter 7.1 設定 https://catalog.workshops.aws/greengrass/en-US/chapter7-containers/10-step1 ### 在EC2上安裝docker * Install docker ``` sudo apt-get update sudo apt-get install docker.io ``` ``` ubuntu@ip-10-0-1-21:~/environment/GreengrassCore/artifacts/com.example.Subscriber/1.0.0$ docker --version Docker version 24.0.7, build 24.0.7-0ubuntu2~22.04.1 ``` * Add the Greengrass user to the Docker group, allowing it to run containers. ``` sudo usermod -aG docker ggc_user ``` ### 提供 Greengrass Core device ECR 權限 * 建立一個名為 workshop_ecr_iot_gg_policy 的 IAM Policy ``` { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "ecr:CreateRepository", "ecr:GetDownloadUrlForLayer", "ecr:BatchGetImage", "ecr:CompleteLayerUpload", "ecr:GetAuthorizationToken", "ecr:UploadLayerPart", "ecr:InitiateLayerUpload", "ecr:BatchCheckLayerAvailability", "ecr:PutImage" ], "Resource": "*" } ] } ``` * 將workshop_ecr_iot_gg_policy掛載給GreengrassV2TokenExchangeRole * 可以參考 [Chapter 3.2](https://hackmd.io/@srcuTWQ_SsWXm3i2P_fHJw/BJThqPGCA#%E5%BB%BA%E7%AB%8B-IAM-Policy) 作法 ![Screenshot 2024-10-03 at 11.56.51 PM](https://hackmd.io/_uploads/ByLCA43A0.png) ## Chapter 7.2 https://catalog.workshops.aws/greengrass/en-US/chapter7-containers/20-step2-publisher * 建立一個publisher component 要上傳的 Elastic Container Registry (ECR) Repository ``` aws ecr create-repository --repository-name publisher-container ``` ``` ubuntu@ip-10-0-1-21:~/environment/GreengrassCore/artifacts/com.example.Subscriber/1.0.0$ aws ecr create-repository --repository-name publisher-container { "repository": { "repositoryArn": "arn:aws:ecr:ap-northeast-1:911551666144:repository/publisher-container", "registryId": "911551666144", "repositoryName": "publisher-container", "repositoryUri": "911551666144.dkr.ecr.ap-northeast-1.amazonaws.com/publisher-container", "createdAt": 1727971115.462, "imageTagMutability": "MUTABLE", "imageScanningConfiguration": { "scanOnPush": false }, "encryptionConfiguration": { "encryptionType": "AES256" } } } ``` * 前往 ECR [console](https://ap-northeast-1.console.aws.amazon.com/ecr/private-registry/repositories?region=ap-northeast-1), 勾選 Publisher Component Repository 並點擊 'View Push Commands'. 複製 login command ![Screenshot 2024-10-03 at 11.59.39 PM](https://hackmd.io/_uploads/B1xrtyS3AA.png) ### 開始建立Component應用程式檔案 * dummy_sensor.py ``` # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. # SPDX-License-Identifier: MIT-0 import numpy as np class DummySensor(object): def __init__(self, mean=25, variance=1): self.mu = mean self.sigma = variance def read_value(self): return np.random.normal(self.mu, self.sigma, 1)[0] if __name__ == '__main__': sensor = DummySensor() print(sensor.read_value()) ``` * example_publisher.py ``` # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. # SPDX-License-Identifier: MIT-0 import time import datetime import json import sys import signal import os import awsiot.greengrasscoreipc from awsiot.greengrasscoreipc.model import ( PublishToTopicRequest, PublishMessage, JsonMessage ) from dummy_sensor import DummySensor #kill process def sighandler(a,b): sys.exit(0) signal.signal(signal.SIGINT | signal.SIGTERM, sighandler) TIMEOUT = 10 publish_rate = 1.0 ipc_client = awsiot.greengrasscoreipc.connect() sensor = DummySensor() topic = "my/topic" while True: message = {"timestamp": str(datetime.datetime.now()), "value": sensor.read_value()} message_json = json.dumps(message).encode('utf-8') request = PublishToTopicRequest() request.topic = topic publish_message = PublishMessage() publish_message.json_message = JsonMessage() publish_message.json_message.message = message request.publish_message = publish_message operation = ipc_client.new_publish_to_topic() operation.activate(request) future = operation.get_response() future.result(TIMEOUT) print(f"publisher is successful! {message_json}") time.sleep(1/publish_rate) ``` * Dockerfile ``` FROM public.ecr.aws/amazonlinux/amazonlinux:latest # Install dependencies RUN yum update -y && \ yum install -y python3 && \ yum install -y python3-pip && \ pip3 install numpy && \ pip3 install awsiotsdk # run selected files RUN mkdir /pyfiles COPY ./dummy_sensor.py /pyfiles/dummy_sensor.py COPY ./example_publisher.py /pyfiles/example_publisher.py EXPOSE 80 CMD ["python3","-u", "/pyfiles/example_publisher.py"] ``` ### Push Component application 到 ECR ![image](https://hackmd.io/_uploads/BJOy16hRC.png) * Build the Docker container EX: ``` docker build -t publisher-container . ``` * Tag the Docker container EX: ``` docker tag publisher-container:latest 911551666144.dkr.ecr.ap-northeast-1.amazonaws.com/publisher-container:latest ``` * Push the local container to the ECR Repository EX: ``` docker push 911551666144.dkr.ecr.ap-northeast-1.amazonaws.com/publisher-container:latest ``` ### Create component of container ``` touch ~/environment/GreengrassCore/recipes/com.example.PublisherContainer-1.0.0.json ``` ``` { "RecipeFormatVersion": "2020-01-25", "ComponentName": "com.example.PublisherContainer", "ComponentVersion": "1.0.0", "ComponentType": "aws.greengrass.generic", "ComponentDescription": "A component that runs a Docker container and uses interprocess communication.", "ComponentPublisher": "Amazon", "ComponentConfiguration": { "DefaultConfiguration": { "accessControl": { "aws.greengrass.ipc.pubsub": { "com.example.PublisherContainer:pubsub:1": { "policyDescription": "Allows access to publish and subscribe to all topics.", "operations": [ "*" ], "resources": [ "*" ] } } } } }, "ComponentDependencies": { "aws.greengrass.DockerApplicationManager": { "VersionRequirement": ">=2.0.0 <2.1.0", "DependencyType": "HARD" }, "aws.greengrass.TokenExchangeService": { "VersionRequirement": ">=2.0.0 <2.1.0", "DependencyType": "HARD" } }, "Manifests": [ { "Platform": { "os": "all" }, "Lifecycle": { "Run": "docker run --rm -t -v /greengrass/v2:/greengrass/v2 -e AWS_REGION -e SVCUID -e AWS_GG_NUCLEUS_DOMAIN_SOCKET_FILEPATH_FOR_COMPONENT -e AWS_CONTAINER_AUTHORIZATION_TOKEN -e AWS_CONTAINER_CREDENTIALS_FULL_URI <ACCOUNT_ID>.dkr.ecr.<REGION>.amazonaws.com/publisher-container:latest" }, "Artifacts": [ { "Uri": "docker:<ACCOUNT_ID>.dkr.ecr.<REGION>.amazonaws.com/publisher-container:latest", "Unarchive": "NONE", "Permission": { "Read": "OWNER", "Execute": "NONE" } } ] } ], "Lifecycle": {} } ``` ## Chapter 7.3 ``` ubuntu@ip-10-0-1-21:~$ aws ecr create-repository --repository-name subscriber-container { "repository": { "repositoryArn": "arn:aws:ecr:ap-northeast-1:911551666144:repository/subscriber-container", "registryId": "911551666144", "repositoryName": "subscriber-container", "repositoryUri": "911551666144.dkr.ecr.ap-northeast-1.amazonaws.com/subscriber-container", "createdAt": 1728004869.495, "imageTagMutability": "MUTABLE", "imageScanningConfiguration": { "scanOnPush": false }, "encryptionConfiguration": { "encryptionType": "AES256" } } } ``` ``` ubuntu@ip-10-0-1-21:~$ aws ecr get-login-password --region ap-northeast-1 | docker login --username AWS --password-stdin 911551666144.dkr.ecr.ap-northeast-1.amazonaws.com WARNING! Your password will be stored unencrypted in /home/ubuntu/.docker/config.json. Configure a credential helper to remove this warning. See https://docs.docker.com/engine/reference/commandline/login/#credentials-store Login Succeeded ubuntu@ip-10-0-1-21:~$ ubuntu@ip-10-0-1-21:~$ ubuntu@ip-10-0-1-21:~$ mkdir -p ~/environment/GreengrassCore/artifacts/com.example.SubscriberContainer/1.0.0 \ && cd ~/environment/GreengrassCore/artifacts/com.example.SubscriberContainer/1.0.0 \ && touch example_subscriber.py Dockerfile ubuntu@ip-10-0-1-21:~/environment/GreengrassCore/artifacts/com.example.SubscriberContainer/1.0.0$ docker build -t subscriber-container . DEPRECATED: The legacy builder is deprecated and will be removed in a future release. Install the buildx component to build images with BuildKit: https://docs.docker.com/go/buildx/ Sending build context to Docker daemon 4.608kB Step 1/6 : FROM public.ecr.aws/amazonlinux/amazonlinux:latest ---> 2001b66b1d2c Step 2/6 : RUN yum update -y && yum install -y python3 && yum install -y python3-pip && pip3 install numpy && pip3 install awsiotsdk ---> Using cache ---> 8254539e5f9c Step 3/6 : RUN mkdir /pyfiles ---> Using cache ---> 0c2982af6656 Step 4/6 : COPY ./example_subscriber.py /pyfiles/example_subscriber.py ---> e0283934aa59 Step 5/6 : EXPOSE 80 ---> Running in c917d543e478 Removing intermediate container c917d543e478 ---> 0e36d973126c Step 6/6 : CMD ["python3","-u", "/pyfiles/example_subscriber.py"] ---> Running in fe5de18ad3c2 Removing intermediate container fe5de18ad3c2 ---> 82d03be039fc Successfully built 82d03be039fc Successfully tagged subscriber-container:latest ubuntu@ip-10-0-1-21:~/environment/GreengrassCore/artifacts/com.example.SubscriberContainer/1.0.0$ ubuntu@ip-10-0-1-21:~/environment/GreengrassCore/artifacts/com.example.SubscriberContainer/1.0.0$ ubuntu@ip-10-0-1-21:~/environment/GreengrassCore/artifacts/com.example.SubscriberContainer/1.0.0$ docker tag subscriber-container:latest 911551666144.dkr.ecr.ap-northeast-1.amazonaws.com/subscriber-container:latest ubuntu@ip-10-0-1-21:~/environment/GreengrassCore/artifacts/com.example.SubscriberContainer/1.0.0$ ubuntu@ip-10-0-1-21:~/environment/GreengrassCore/artifacts/com.example.SubscriberContainer/1.0.0$ ubuntu@ip-10-0-1-21:~/environment/GreengrassCore/artifacts/com.example.SubscriberContainer/1.0.0$ docker push 911551666144.dkr.ecr.ap-northeast-1.amazonaws.com/subscriber-container:latest The push refers to repository [911551666144.dkr.ecr.ap-northeast-1.amazonaws.com/subscriber-container] bb0886f02a66: Pushed 6f7c13e150fc: Pushed a2b5465ec5f4: Pushed e35bf179f8b0: Pushed latest: digest: sha256:23fd7c2c12d219bc9e8564cd8e1669c54f4d8f404e0a6f31a90628b51b721439 size: 1155 ubuntu@ip-10-0-1-21:~/environment/GreengrassCore/artifacts/com.example.SubscriberContainer/1.0.0$ ``` ## Chapter 7.4