# Hands-on : Azure Kubernetes Service and CI/CD with Azure DevOps ###### tags: `台新` `Workshop` `Azure Kubernetes Service` `Container Registry` `Azure DevOps` [ToC] ## 1/ 使用 Azure Kubernetes Service 部署 Kubernetes ### 使用 Azure Cloud Shell - 開啟 [Azure Portal](https://ms.portal.azure.com/#home) 上的 Azure Cloud Shell ![](https://i.imgur.com/9gmLo69.png) - 首次使用會建立 Azure Blob Storage,選擇要使用的訂閱,點選建立即可 - 確定切換置 bash ![](https://i.imgur.com/jAyqBbV.png) :::info :bulb: **Hint :** 使用 Azure Cloud Shell 時需使用 `ctrl` + `c` 複製 及 `shift` + `ctrl` + `v` 貼上 ::: ### 建立新的資源群組 - 在整個部署指令碼中會重複使用某些值。舉例來說,須選擇要建立資源群組的區域,例如 `East US`,可自行修改參數值。 ```azurecli-interactive REGION_NAME=southeastasia RESOURCE_GROUP=cicdworkshop SUBNET_NAME=cicd-subnet VNET_NAME=cicd-vnet ``` - 地區也可使用 `REGION_NAME=eastus` - 可透過 `echo $REGION_NAME` 顯示參數是否確定被輸入正確的值 - 使用 `aksworkshop` 作為名稱來建立新的資源群組。 於此資源群組中部署練習中建立的所有資源 ```azurecli-interactive az group create \ --name $RESOURCE_GROUP \ --location $REGION_NAME ``` ### 建立 AKS Cluster <!-- - 註冊 Monitoring ``` az provider register --namespace Microsoft.OperationsManagement az provider register --namespace Microsoft.OperationalInsights az provider register --namespace Microsoft.Insights ``` --> - 設定 AKS cluster 名稱,此名稱在使用的訂閱中需為唯一 ```azurecli-interactive AKS_CLUSTER_NAME=aks-cicd-$RANDOM ``` - 取得隨機產生的 AKS Cluster 名稱,並將此值記下 ```azurecli-interactive echo $AKS_CLUSTER_NAME ``` - 執行以下命令來建立最新版本的 AKS Cluster,此需花費幾分鐘的時間才能完成 ``` az aks create \ --resource-group $RESOURCE_GROUP \ --name $AKS_CLUSTER_NAME \ --location $REGION_NAME \ --enable-managed-identity \ --node-count 1 \ --node-vm-size Standard_D2s_v3 \ --generate-ssh-keys ``` <!-- - 執行以下命令來建立最新版本的 AKS Cluster,此需花費幾分鐘的時間才能完成 ``` az aks create \ --resource-group $RESOURCE_GROUP \ --name $AKS_CLUSTER_NAME \ --location $REGION_NAME \ --enable-managed-identity \ --node-count 1 \ --node-vm-size Standard_D2s_v3 \ --enable-addons monitoring \ --enable-msi-auth-for-monitoring \ --generate-ssh-keys ``` --> ### 使用 ```kubectl``` 測試叢集連線能力 kubectl 是用來與 Cluster 互動並可在 Cloud Shell 中使用 - 使用 `az aks get-credentials` 命令來設定 kubectl 的執行個體 ```azurecli-interactive az aks get-credentials \ --resource-group $RESOURCE_GROUP \ --name $AKS_CLUSTER_NAME ``` - 列出所有節點 ```azurecli-interactive kubectl get nodes ``` - 建立 `voting` 的 Namespace ```azurecli-interactive kubectl create namespace voting ``` ## 2/ 建立私人容器登錄 Azure Container Registry - 設定 ACR 的名稱,在 Azure 中須為唯一,這裡使用 random 的方式為 ACR 命名 ```azurecli-interactive ACR_NAME=acrcicd$RANDOM ``` - 輸出 ACR_NAME 並記錄下來 ```azurecli-interactive echo $ACR_NAME ``` - 透過 `az acr create` 建立與 AKS 資源位於相同資源群組及區域中的 ACR ```azurecli-interactive az acr create \ --resource-group $RESOURCE_GROUP \ --location $REGION_NAME \ --name $ACR_NAME \ --sku Standard ``` ## 3/ 使用 Azure Container Registry 工作建立容器映像 Fruit Smoothies 評等應用程式使用兩個容器映像,一個用於前端網站,另一個用於 RESTful API Web 服務,接下來會使用 Azure Container Registry 以透過標準 Dockerfile 提供建置指示來建置這些容器映像 ### 建立 Ratings API 映像 - 先建立一個資料夾提供給今天練習的範例程式碼使用 ```azurecli-interactive mkdir aks-cicd-lab ``` - 索引到 `aks-cicd-lab` 底下 ```azurecli-interactive cd aks-cicd-lab ``` - 下載範例 API 程式碼 ```azurecli-interactive git clone https://github.com/Azure-Samples/azure-voting-app-redis.git ``` - 索引至範例程式碼目錄下 ```azurecli-interactive cd azure-voting-app-redis/azure-vote ``` - 執行 `az acr build`,此指令會使用 Dockerfile 來建置容器映像,完成後將其推送至 ACR 中存放 ```azurecli-interactive az acr build \ --resource-group $RESOURCE_GROUP \ --registry $ACR_NAME \ --image azure-voting-app-redis:v1 . ``` :::info :bulb: **注意:** 不要忘記上述命令結尾的句點 `.`, 其代表包含 Dockerfile 的來源目錄 ::: ### 驗證映像 - 執行以下命令確認映像已建立於 ACR 中 ```azurecli-interactive az acr repository list \ --name $ACR_NAME \ --output table ``` - 輸出 ```azurecli-interactive Result ---------------------- azure-voting-app-redis ``` ### 設定 AKS Cluster 與 ACR 間的驗證及身分授權 - 透過自動產生驗證方式 (需為 subscription owner) 需要在容器登錄與 Kubernetes 叢集之間設定驗證,以允許服務之間的通訊 - 執行 `az aks update` 命令來自動設定兩個資源間的必要服務主體驗證,此步驟會花費一些時間做 AKS 設定的更新 ```azurecli-interactive az aks update \ --name $AKS_CLUSTER_NAME \ --resource-group $RESOURCE_GROUP \ --attach-acr $ACR_NAME ``` - **(OPTION)** 自行設定連線 Container Registry secret 方式 - 將 ACR 啟用 admin account ```azurecli-interactive az acr update -n $ACR_NAME --admin-enabled true ``` - 取得 ACR 連線資訊 ```azurecli-interactive ACR_UNAME=$(az acr credential show -n $ACR_NAME --query="username" -o tsv) ACR_PASSWD=$(az acr credential show -n $ACR_NAME --query="passwords[0].value" -o tsv) ``` - 將 ACR 連線資訊設定於 K8S ```azurecli-interactive kubectl create secret docker-registry acr-secret \ --docker-server=$ACR_NAME \ --docker-username=$ACR_UNAME \ --docker-password=$ACR_PASSWD ``` - Assign K8S secret to default service account ```azurecli-interactive kubectl patch serviceaccount default -p '{"imagePullSecrets": [{"name": "acr-secret"}]}' ``` ## 4/ 手動部屬服務 - 索引到程式碼目錄下 ```azurecli-interactive cd ~/aks-cicd-lab/azure-voting-app-redis/ ``` - 新增後端部屬設定 `azure-vote-back.yaml`,並部屬 - 新增 ```azurecli-interactive code azure-vote-back.yaml ``` - 將以下貼入 ```yaml apiVersion: apps/v1 kind: Deployment metadata: name: azure-vote-back spec: replicas: 1 selector: matchLabels: app: azure-vote-back template: metadata: labels: app: azure-vote-back spec: nodeSelector: "beta.kubernetes.io/os": linux containers: - name: azure-vote-back image: mcr.microsoft.com/oss/bitnami/redis:6.0.8 env: - name: ALLOW_EMPTY_PASSWORD value: "yes" ports: - containerPort: 6379 name: redis --- apiVersion: v1 kind: Service metadata: name: azure-vote-back spec: ports: - port: 6379 selector: app: azure-vote-back ``` - 部屬至 AKS ```azurecli-interactive kubectl apply -f azure-vote-back.yaml -n voting ``` <!-- - 透過 `azure-vote-all-in-one-redis.yaml` 一次性的部屬所有服務 ```azurecli-interactive kubectl apply -f azure-vote-all-in-one-redis.yaml -n voting ``` --> - 建立並部屬前端 deployment 檔案 - 新增 `azure-vote-front-deployment.yaml` ```azurecli-interactive code azure-vote-front-deployment.yaml ``` - 將以下檔案貼上,完成後儲存 ```yaml apiVersion: apps/v1 kind: Deployment metadata: name: azure-vote-front spec: replicas: 1 selector: matchLabels: app: azure-vote-front strategy: rollingUpdate: maxSurge: 1 maxUnavailable: 1 minReadySeconds: 5 template: metadata: labels: app: azure-vote-front spec: nodeSelector: "beta.kubernetes.io/os": linux containers: - name: azure-vote-front image: <acrname>.azurecr.io/azure-voting-app-redis:v1 ports: - containerPort: 80 resources: requests: cpu: 250m limits: cpu: 500m env: - name: REDIS value: "azure-vote-back" ``` - 將 `<acrname>` 取代為先前建立的 ACR 名稱 ![](https://i.imgur.com/VCfc4ib.png) - 部屬至 AKS ```azurecli-interactive kubectl apply -f azure-vote-front-deployment.yaml -n voting ``` - 新增前端 service 設定並部屬 - 新增 `azure-vote-front-service.yaml` ```azurecli-interactive code azure-vote-front-service.yaml ``` - 將以下檔案貼上,完成後儲存 ```yaml apiVersion: v1 kind: Service metadata: name: azure-vote-front spec: type: LoadBalancer ports: - port: 80 selector: app: azure-vote-front ``` - 部屬至 AKS ```azurecli-interactive kubectl apply -f azure-vote-front-service.yaml -n voting ``` - 取得 External IP,並驗證網頁正常運行 ``` kubectl get service -n voting -w ``` ## 5/ AKS with Azure DevOps CI/CD ### 版本控制 #### 匯入範例程式碼 - 至 [Azure DevOps](https://dev.azure.com/) 連結至先前建立的 Project,或新增一個 - 點選左方欄位 Repo 底下的 **[Files]**,選擇上方的repo name,點選 **[+New repository]** ![](https://i.imgur.com/Bvyk9We.png) - 將 **Repository Name** 填寫成 `azure-voting-app-redis`,並將 **[Add a README]** 的勾取消,再點選 **[Create]** ![](https://i.imgur.com/183usQQ.png =350x) - 複製該 repo 連結 ![](https://i.imgur.com/pLm2DSW.png) <!-- - 點選畫面中 **[Import]** 的按鈕,並複製以下連結貼到 **[Clone URL]** 中,完成後點選 **[Import]** ``` https://github.com/MicrosoftDocs/mslearn-aks-workshop-ratings-api.git ``` ![](https://i.imgur.com/lXX4IVf.png) - 顯示正在進行匯入 ![](https://i.imgur.com/GGBJlzi.png) --> - 回到 Azure Cloud Shell,索引到程式碼目錄 ``` cd ~/aks-cicd-lab/azure-voting-app-redis/ ``` - 環境中第一次設置 git (# 置換成編號) ``` git config --global user.email "workshop-user-tw2023-#@huier23outlook.onmicrosoft.com" git config --global user.name "workshop-user-tw2023-#" ``` - 將新的檔案加入版控中 ``` git add . git commit -m "add k8s config" ``` - 將遠端儲存庫設定到本機 git 中 ``` git remote add devops https://<organization name>@dev.azure.com/<organization name>/<project name>/_git/azure-voting-app-redis ``` - 輸入以下指令可確認已成功設定 ``` git remote -v ``` ![](https://i.imgur.com/7rV9pfY.png) - 開始推送至 Azure Repo ``` git push devops master ``` - 視窗會顯示詢問密碼,選擇下方兩種方式之一取得 PAT ``` Password for 'https://huier-teamservice@dev.azure.com': ``` 1. 使用 PAT - 回到 Azure DevOps Service,點選右上角的圖示,並選擇 Personal Access Token ![](https://i.imgur.com/ncvLG8S.png =300x) - 點選 **[+New Token]**,完成資訊的填寫 ![](https://i.imgur.com/I9EwWuD.png) - 將 token 複製起來,並記在記事本中(一旦關閉則會消失) ![](https://i.imgur.com/MEy9Gt9.png =400x) 2. 使用 Generate Git Credentials - 點選 ![](https://i.imgur.com/PzNAX3B.png =160x) ![](https://i.imgur.com/6hOOj3W.png) - 將 token 複製起來,並記在記事本中 - 回到 Azure Cloud Shell 並將 token 貼上 (貼上後不會顯示任何東西) ![](https://i.imgur.com/FsXp2wN.png) ### 建立 Build Pipeline - 選擇左方選單列中 Pipelines 下的 **[Pipelines]**,點選 **[Create Pipeline]** ![](https://i.imgur.com/5RQojNb.png) - 選擇畫面下方說明文字中的 **[Use the classic editor]** ![](https://i.imgur.com/9FycaSt.png =400x) - 選擇 `azure-voting-app-redis` 的 repository ![](https://i.imgur.com/GW3ourU.png) - 在右上方的搜尋欄位尋找 `docker`,找到 **Docker Container**,點選 **[Apply]** ![](https://i.imgur.com/MWxKFga.png) - 選到 Build an image,完成內容 - **Azure Subscription** : **<建立 ACR 資源時使用的訂閱>** - **Azure Container Registry** : 選擇當初建立的 ACR 名稱 - **Docker File** : `**/azure-vote/Dockerfile` - **Image Name** : `$(Build.Repository.Name):$(Build.BuildId)` ![](https://i.imgur.com/CzCJWrv.png) <!-- - **Image Name** : `$(REPO)-api:$(Build.BuildNumber)` --> <!-- ![](https://i.imgur.com/QwNgMbr.png) --> - 選到 Push an image,完成內容 - **Azure Subscription** : **<建立 ACR 資源時使用的訂閱>** - **Azure Container Registry** : 選擇當初建立的 ACR 名稱 - **Docker File** : `**/azure-vote/Dockerfile` - **Image Name** : `$(Build.Repository.Name):$(Build.BuildId)` ![](https://i.imgur.com/ruew1bz.png) <!-- ![](https://i.imgur.com/QLGze24.png) --> - 點選 agent job 的 + 添加一個 task,在搜尋欄搜尋 `copy`,選擇 **Copy files** ![](https://i.imgur.com/9U2dgTW.png) - 設定 Copy files - **Source Folder** : `$(Build.Repository.LocalPath)` - **Contents** : `**.yaml` - **Target Folder** : `$(Build.ArtifactStagingDirectory)` ![](https://i.imgur.com/3kTBIxD.png) - 再添加一個 task,搜尋 `publish`,選擇 **Publish build artifacts** ![](https://i.imgur.com/BONhx5i.png) - 設定 Publish build artifacts - **Path to publish** : `$(Build.ArtifactStagingDirectory)` - **Artifact name** : `drop-$(Build.BuildNumber)` ![](https://i.imgur.com/pgtV0RY.png) <!-- - 設定 Variable,選擇 Variables > Variable groups > Manage variable groups ![](https://i.imgur.com/ngJKu01.png =500x) - 點選 ![](https://i.imgur.com/C06uZa9.png =130x),設定變數,完成後點選 ![](https://i.imgur.com/MH12mhk.png =70x) - **Variable group name**: `aks-lab` (或任意命名) - ***Variables*** | **Name** | **Value** | | -------- | -------- | | `ACR` | `acrxxxx.azurecr.io` | | `REPO` | `ratings` | ![](https://i.imgur.com/wdZhcRa.png) - 回到 Pipeline > Variables > Variable groups > ![](https://i.imgur.com/kFMiDNM.png =150x) > 剛剛建立的 variable group > Link ![](https://i.imgur.com/pEtXIfh.png) --> - 選擇 Pipeline > Agent Specification > ubuntu-latest ![](https://i.imgur.com/z9pTdOn.png) - 選擇上方列表的 **[Triggers]** tab,將畫面右方的 **[Enable continuous integration]** 打勾 ![](https://i.imgur.com/LfSoWnh.png) - 在 Path filters 下選擇 **+Add**,選擇 **Exclude**,並在後方欄位填上 `**.yaml` ![](https://i.imgur.com/cYNLakF.png =400x) - 完成後修改 pipeline 名稱為 `azure-voting-CI`,點選上方的 **[Save & queue]** ,選擇 **[Save & queue]** 後在跳出的視窗點選 **[Save and run]** ![](https://i.imgur.com/OMHwqUH.png) ![](https://i.imgur.com/FoX0mX6.png) - 回到 Azure Portal 上確認有新的 Repository 及 Image ![](https://i.imgur.com/L5D92e2.png) <!-- #### 建立 **mslearn-aks-workshop-ratings-web** 的 Build Pipeline - 點選右上角 ![](https://i.imgur.com/V58Hac2.png =100x) 再新增一條提供 web 建置,同樣的步驟再重複一遍完成 - 選擇畫面下方說明文字中的 **[Use the classic editor]** - 選擇 `mslearn-aks-workshop-ratings-web` 的 repository ![](https://i.imgur.com/AotCfSo.png =500x) - 在右上方的搜尋欄位尋找 `docker`,找到 **Docker Container**,點選 **[Apply]** - 選到 Build an image,完成內容 - **Azure Subscription** : **<建立 ACR 資源時使用的訂閱>** - **Azure Container Registry** : `$(ACR)` - **Image Name** : `$(REPO)-web:$(Build.BuildNumber)` ![](https://i.imgur.com/b1hnzdY.png) - 選到 Push an image,完成內容 - **Azure Subscription** : **<建立 ACR 資源時使用的訂閱>** - **Azure Container Registry** : `$(ACR)` - **Image Name** : `$(REPO)-web:$(Build.BuildNumber)` ![](https://i.imgur.com/ipZ5dSn.png) - 點選 agent job 的 + 添加一個 task,在搜尋欄搜尋 `copy`,選擇 **Copy files** - 設定 Copy files - **Source Folder** : `$(Build.Repository.LocalPath)` - **Contents** : `**.yaml` - **Target Folder** : `$(Build.ArtifactStagingDirectory)` ![](https://i.imgur.com/fXbx5xc.png) - 再添加一個 task,搜尋 `publish`,選擇 **Publish build artifacts** - 設定 Publish build artifacts - **Path to publish** : `$(Build.ArtifactStagingDirectory)` - **Artifact name** : `drop-$(Build.BuildNumber)` ![](https://i.imgur.com/tGPddY6.png) - 設定 Variable,選擇 Variables > Variable groups > ![](https://i.imgur.com/kFMiDNM.png =150x) > 剛剛建立的 variable group > Link ![](https://i.imgur.com/VlF2xlb.png) - 選擇 Task > Pipeline > Agent Specification > ubuntu-latest - 選擇上方列表的 **[Triggers]** tab,將畫面右方的 **[Enable continuous integration]** 打勾 - 在 Path filters 下選擇 **+Add**,選擇 **Include**,並在後方欄位填上 `src/` ![](https://i.imgur.com/88aZ297.png =400x) - 完成後修改 pipeline 名稱為 `ratings-web-CI`,點選上方的 **[Save & queue]** ,選擇 **[Save & queue]** 後在跳出的視窗點選 **[Save and run]** --> ### 建立 Azure DevOps 對 AKS 的 Service Connection - 點選 **Project Setting** - 選擇 **Service Connections** - 點選 ![](https://i.imgur.com/T3j0PEY.png =180x) - 選擇 **Kubernetes** 並點選 **Next** ![](https://i.imgur.com/aaXHNo7.png =500x) - 設定連線有兩種方式: - **Authentication method 選擇 Azure Subscription** 1. Azure Subscription 選擇建立 AKS 的訂閱 2. Cluster 選擇建立的 AKS 3. Namespace 設定 `voting` 4. 將 **Use cluster admin credentials** 做勾選 5. Service connection name 自行設定 ![](https://i.imgur.com/nO2NcZO.png =450x) - **Authentication method 選擇 KubeConfig** 1. 到 Cloud Shell 上,使用以下指令開啟 KubeConfig ``` code /home/workshop-user-tw2023-#/.kube/config ``` 2. 複製所有內容並貼到 Azure DevOps 中的 KubeCofig 欄位 3. 點選 ![](https://i.imgur.com/W2drMIj.png =50x),確認出現 ![](https://i.imgur.com/in5Zgom.png =180x) 4. 輸入 Service Connection name 後點選 ![](https://i.imgur.com/GsZPWrS.png =100x) ![](https://i.imgur.com/tALC0L3.png =450x) ### 建立 Release Pipeline - 在左方選單列選擇 Pipelines 下的 **[Release]**,進入後點選 **[New Pipeline]** ![](https://i.imgur.com/WgaXG4O.png) - 選擇 ![](https://i.imgur.com/cMueEbe.png =100x) ![](https://i.imgur.com/iSGXH81.png =400x) - 將 Stage name 更改為 `Prod`,完成後點選 **[X]** 關閉視窗 ![](https://i.imgur.com/2oAPXgQ.png =400x) - 選擇 Source Type 為 **Build**,Source (build pipeline) 為先前建立的 Pipeline,記下 Source alias 的資料,完成後點選 **[Add]** ![](https://i.imgur.com/mcTwx2v.png) - 記下 source alias 資訊 ![](https://i.imgur.com/QO3Ud7O.png =400x) - 點選 artifact 右上方的 ![](https://i.imgur.com/RAfgToy.png =40x) 設定 Continuous deployment trigger,設置為 Enabled ![](https://i.imgur.com/KPXqz7g.png) - 選擇 `Prod` stage 內的 **[1 job, 0 task]** ![](https://i.imgur.com/6ilJKlD.png =500x) - 點選 Agent Job 的 **[+]**,搜尋 `kube`,找到 **Deploy to Kubernetes** 點選 **[Add]** ![](https://i.imgur.com/2PHTATD.png) - 點選 ![](https://i.imgur.com/AkLxJhJ.png =200x) task 進行設定 - **Kubernetes service connection**:先前建立的 service connection - **Namespace**: `voting` - **Manifests**: `$(System.DefaultWorkingDirectory)/**/**/azure-vote-front-deployment.yaml` - **Containers**: `<ACR 名稱>/<ACR 中 repository 的名稱>:$(Release.Artifacts._azure-voting-CI.BuildId)` (`_azure-voting-CI` 的部份確認與你的 Source alias 一致) - **Timeout for rollout status**: `60` ![](https://i.imgur.com/sSiIbFL.png) <!-- - 設定變數,Variables > variable groups > ![](https://i.imgur.com/N8I9N64.png =150x) > 先前建立的 variable group > Link ![](https://i.imgur.com/W6ZBoi4.png) --> - 點選上方 Pipeline,在 Prod 的 stage 中點選![](https://i.imgur.com/yMs6J4t.png =35x),在出的視窗中將 **[Pre-deployment approvals]** 改為 **[Enabled]**,並在 **[Approvers]** 欄位中選擇自己,完成後點選 **[X]** 關閉視窗 ![](https://i.imgur.com/rLMjwFj.png) - 將上方 All Pipelines > **[New release pipeline]** 修改為 `Release web to AKS` ![](https://i.imgur.com/P4CHXvg.png =400x) - 點選右上角 Save ![](https://i.imgur.com/hssFc8S.png) - 接著選擇 **[Create Release]** - 在跳出的視窗中點選 **[Create]** - 視窗上方會有一則訊息,點選 **[release 1]**,可以查看目前部屬進度 ![](https://i.imgur.com/vVhOk6B.png) - 點選 ![](https://i.imgur.com/4W3tqzW.png =80x) 允許部屬 ![](https://i.imgur.com/PsNvtjf.png) - 點選 Logs 查看部屬狀況 ![](https://i.imgur.com/Jkw0cb1.png) - 完成後瀏覽到你既有的網站確認網站依然顯示正常 - 回到 Azure Cloud Shell - 輸入以下命令查看目前的 pods ``` kubectl describe pods azure-vote-front-xxxx -n voting ``` - 查看前端 Pod 的 tag 已有異動 ``` kubectl ``` ![](https://i.imgur.com/6whK4sd.png) ## 6/ 驗證流程 #### 將首頁加上今天日期 - Azure Repo > azure-voting-app-redis > `azure-vote/azure-vote/config_file.cfg` ```cfg # UI Configurations TITLE = 'Azure Voting App - <今天日期>' VOTE1VALUE = 'Cats' VOTE2VALUE = 'Dogs' SHOWHOST = 'false' ``` ![](https://i.imgur.com/ftkxsTY.png) - 完成後 commit 確認 Pipeline 有自動啟動 - 可透過 `describe` 指令確認 image 版本 ``` kubectl describe pod azure-vote-front-xxxxx -n voting ``` ![](https://i.imgur.com/IihlJRc.png)