顯示具有 service 標籤的文章。 顯示所有文章
顯示具有 service 標籤的文章。 顯示所有文章

2025年2月17日 星期一

Docker 開發筆記 - 使用 aws cli 和 Docker Exec 進入 AWS ECS Container @ macOS

這個議題比我想像中麻煩了點,讓我回憶起 2009 年寫的 AWS 筆記,那時同事一開始還只在用 Firefox extension 管理 AWS EC2 呢 XD 我覺得這理論上要能都透過網頁搞定才對,先把目前研究的過程筆記一下。

總之,要能像 ssh 遠端(docker exec -it ContainerID bash)進去 AWS ECS container 的關鍵之處:
  • 使用 awscli 做事
  • AWS ECS 的 Task 定義,基礎設施需求 -> 任務角色,需要指定一下角色,例如 ecsTaskExecutionRole
  • AWS IAM -> ecsTaskExecutionRole -> 添加 AmazonSSMManagedInstanceCore 權限
  • AWS ECS -> Cluster -> Service ,需要用 awscli 啟動 enable-execute-command ,並且重新更新服務,使之生效
  • 使用 aws 指令登入
首先,先下載 awscli 並且版本要夠新:
安裝後檢查版本,版本太低會無法完成任務:

% aws --version
aws-cli/2.24.5 Python/3.12.6 Darwin/24.3.0 exe/x86_64

接著還要安裝 Session Manager plugin,此例紀錄 Mac with Apple silicon 版:
% curl "https://s3.amazonaws.com/session-manager-downloads/plugin/latest/mac_arm64/sessionmanager-bundle.zip" -o "sessionmanager-bundle.zip"
% unzip sessionmanager-bundle.zip
% sudo ./sessionmanager-bundle/install -i /usr/local/sessionmanagerplugin -b /usr/local/bin/session-manager-plugin

基本的環境已準備好了,下一刻是查看自己的 AWS ECS 的任務定義是否有把 任務角色 設定好,這部就維持用網頁吧:



若你的 AWS ECS 上定義的 Task 只有一個,也可以偷懶靠 awscli 操作(在此就不贅述 AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_DEFAULT_REGION 部分),會用到的指令:

aws ecs list-task-definition-families --status ACTIVE
aws ecs list-task-definitions --family-prefix webapp
aws ecs describe-task-definition --task-definition webapp:3

連續技,快速檢查 taskRoleArn 跟 executionRoleArn:

% NamespaceID=$(aws ecs list-task-definition-families --status ACTIVE | jq -r '.families[0]') ; echo "Namespace: $NamespaceID" ; TaskID=$(aws ecs list-task-definitions --family-prefix "$NamespaceID" | jq -r '.taskDefinitionArns[0]') ; echo "Task: $TaskID" ; aws ecs describe-task-definition --task-definition "$TaskID" | jq ".taskDefinition | { taskRoleArn: .taskRoleArn, executionRoleArn: .executionRoleArn}"
Namespace: myapp-task
Task: arn:aws:ecs:ap-northeast-1:####:task-definition/myapp-task:2
{
  "taskRoleArn": "arn:aws:iam::####:role/ecsTaskExecutionRole",
  "executionRoleArn": "arn:aws:iam::####:role/ecsTaskExecutionRole"
}

接下來,若 AWS ECS Cluster 還沒有建立任何 Service,用指令查:

% aws ecs list-tasks --cluster myapp-cluster  
{
    "taskArns": []
}

接著我們在 AWS ECS 網頁端起了一個 Service 名為 myapp-service ,在創建過程中沒看到啟用 enable-execute-command,這時在網頁上查看也是顯示 "ECS 執行: 關閉"



接下來用 aws cli 查詢:

% aws ecs list-tasks --cluster myapp-cluster   
{
    "taskArns": [
        "arn:aws:ecs:ap-northeast-1:####:task/myapp-cluster/#TASKID#"
    ]
}

% TaskID=$(aws ecs list-tasks --cluster myapp-cluster | jq -r '.taskArns[0]') ; echo "TaskID: $TaskID" ; aws ecs describe-tasks --cluster myapp-cluster --tasks $TaskID | jq '.tasks[0] | { "clusterArn": .clusterArn, "taskArn": .taskArn, "taskDefinitionArn": .taskDefinitionArn, "group": .group, "healthStatus": .healthStatus, "desiredStatus": .desiredStatus, "enableExecuteCommand": .enableExecuteCommand, "containers-name": [.containers |.[] | { "name":.name, "runtimeId": .runtimeId} ] }'
TaskID: arn:aws:ecs:ap-northeast-1:####:task/myapp-cluster/#TASKID#
{
  "clusterArn": "arn:aws:ecs:ap-northeast-1:####:cluster/myapp-cluster",
  "taskArn": "arn:aws:ecs:ap-northeast-1:####:task/myapp-cluster/#TASKID#",
  "taskDefinitionArn": "arn:aws:ecs:ap-northeast-1:####:task-definition/myapp-task:2",
  "group": "service:myapp-service",
  "healthStatus": "HEALTHY",
  "desiredStatus": "RUNNING",
  "enableExecuteCommand": false,
  "containers-name": [
    {
      "name": "php-fpm-docker",
      "runtimeId": "#TASKID#-#ContainerID#"
    },
    {
      "name": "web-docker",
      "runtimeId": "#TASKID#-#ContainerID#"
    }
  ]
}

可以看到 enableExecuteCommand 為 false

這時候,如果透過 aws cli 來設法登入到 Container:

% aws ecs execute-command --cluster myapp-cluster --task #TASKID# --container #containers#name# --command "/bin/bash" --interactive


The Session Manager plugin was installed successfully. Use the AWS CLI to start a session.


An error occurred (InvalidParameterException) when calling the ExecuteCommand operation: Unable to start session because the container doesn’t exist. Specify a valid container and try again.

接著,使用 aws cli 來啟動 enableExecuteCommand 吧:

% aws ecs update-service --cluster myapp-cluster --service arn:aws:ecs:ap-northeast-1:####:service/myapp-cluster/myapp-service --enable-execute-command 
{
    "service": {
        ... 
        "enableExecuteCommand": true,
        ...
    }
}

可以看到 enableExecuteCommand 被標記成 true 了,這時還需要重新發布服務,可以重網頁去觸發,或是靠指令觸發:

% aws ecs update-service --cluster myapp-cluster --service arn:aws:ecs:ap-northeast-1:####:service/myapp-cluster/myapp-service --force-new-deployment

當服務發布完畢後,在網頁上就可以看到改變,或是用指令在查一次:

% TaskID=$(aws ecs list-tasks --cluster myapp-cluster | jq -r '.taskArns[0]') ; echo "TaskID: $TaskID" ; aws ecs describe-tasks --cluster myapp-cluster --tasks $TaskID | jq '.tasks[0] | { "clusterArn": .clusterArn, "taskArn": .taskArn, "taskDefinitionArn": .taskDefinitionArn, "group": .group, "healthStatus": .healthStatus, "desiredStatus": .desiredStatus, "enableExecuteCommand": .enableExecuteCommand, "containers-name": [.containers |.[] | { "name":.name, "runtimeId": .runtimeId} ] }'
TaskID: arn:aws:ecs:ap-northeast-1:####:task/myapp-cluster/#TASKID#
{
  "clusterArn": "arn:aws:ecs:ap-northeast-1:####:cluster/myapp-cluster",
  "taskArn": "arn:aws:ecs:ap-northeast-1:####:task/myapp-cluster/#TASKID#",
  "taskDefinitionArn": "arn:aws:ecs:ap-northeast-1:####:task-definition/myapp-task:2",
  "group": "service:myapp-service",
  "healthStatus": "HEALTHY",
  "desiredStatus": "RUNNING",
  "enableExecuteCommand": true,
  "containers-name": [
    {
      "name": "php-fpm-docker",
      "runtimeId": "#TASKID#-#CONTAINERID#"
    },
    {
      "name": "web-docker",
      "runtimeId": "#TASKID#-#CONTAINERID#"
    }
  ]
}

如此,就可以正式遠端進去一下:

% aws ecs execute-command --cluster myapp-cluster --task #TASKID# --container web-docker --command "/bin/bash" --interactive

The Session Manager plugin was installed successfully. Use the AWS CLI to start a session.


Starting session with SessionId: ecs-execute-command-################
ip-123-45-6-123:/var/www/html# 

收工

參考資料:

2022年5月16日 星期一

Go 開發筆記 - 地圖服務 查詢剩餘快篩劑販售地點 為例




之前一直想練習 Golang 卻一直偷懶,這次就找個題目練了一下。包括如何取得網路上的資料,以及處理 csv 的流程。然而,最後想找 Heroku 來發布,就...全部改成 HTML+JS 處理而已,但還是留點紀錄吧。

若單純想要使用 政府資料開放平臺 - 健保特約機構防疫家用快篩剩餘數量明細 來作為範例時,其實可以只有一個 HTML 檔案就搞定,因為政府資料開放平臺下載 CSV 的那隻,支援大家隨意呼叫?我是用 Golang 寫完 backend api 後,為了發布到 Heroku 備忘時,就乾脆多寫個直接呼叫的版本

// https://data.gov.tw/dataset/152408
% curl -I https://data.nhi.gov.tw/resource/Nhi_Fst/Fstdata.csv
HTTP/1.1 200 OK
...
Access-Control-Allow-Origin: *
...

而 Golang 的部分,單純使用 gin framework 而已,主程式極短,單純吐 HTML 出去。

最後 templates/index.tmpl 就是 HTML/CSS/JS 的領域而已,分別使用了

- bootstrap@5.1.3
- leaflet@1.8.0
- leaflet.markercluster@1.5.3
- jquery@3.6.0
- jquery-csv@1.0.21

就簡單的桌面版型交給 Bootstrap (對的,沒有mobile版型),地圖則是使用 OpenStreetMap 服務,搭配 leaflet js sdk 管理,最後再用個 jQuery 處理 api 詢問,以及 jquery-csv 處理 CSV 格式。

原本用 Golang 開發 api 時,做了一些 api cache 管理跟 csv parsing 等,因發布到 Heroku 時,簡化成純前端任務就沒在使用了。不然在 Golang 還有規劃避免太頻繁詢問 data.nhi.gov.tw 要資料的架構。

2017年6月15日 星期四

透過 Nginx proxy_pass 架構,進行 Service migration

有一個舊網站已活了數年,改造的方向不外乎提升 SEO、移除不需要的檔案、增加系統安全、架構拆分,或是基本的 Web Framework 的抽換使用等等。對於一個 IoT 產業來說,包袱多多,像是 device 不見得會更新到新版,更別說會跟隨 HTTP 301/302 去取得新資源,甚至新網站的開發還沒辦法一步到位全部切換,只好透過 Nginx proxy_pass 架構去處理相容並扛流量了。

在此使用 upstream 管理舊機器們,並且添加新的 log format 多紀錄 $upstream_addr 資訊以便後續維護:

log_format add_proxy_pass '$remote_addr - $remote_user [$time_local] [=$upstream_addr=] "$request" ' '$status $body_bytes_sent "$http_referer" ' '"$http_user_agent" "$http_x_forwarded_for"';

resolver 8.8.8.8;
upstream oldserver {
hostname1;
hostname2;
hostname3;
}


後續,都是在 nginx server {} 定義範圍下。

更改 log 記錄格式:

access_log  /var/log/nginx/access.log add_proxy_pass;

預期 http client 能處理 HTTP 301/302 的服務位置,給予更佳的 SEO URL:

location ~ old_page\.php$ {
return 301 $scheme://$host/new/page/;
}


對於不能用 HTTP 301/302 的,則改用 proxy_pass 機制:

location ~ ^/old/resource {
proxy_pass http://oldserver$uri;
}


對於,有些文件很清楚要更改內文關鍵字的,可以善用 ngx_http_sub_module:

location /old/resource/file\.json {
proxy_pass http://oldserver$uri;
sub_filter_types *;
sub_filter_once off;
sub_filter '//old.hostname/' '//cdn.hostname/';
}


也可以順順換成 CDN 架構囉。

2014年5月7日 星期三

AWS 筆記 - 使用 Amazon EC2 進行 Deploying Web services

稍微把玩 AWS Elastic Load Balancing 跟 Auto Scaling 後,大概有一點粗淺的心得:
  • 開一台機器,如 micro 等級,並設定這台機器關機也不下線,可專門用於製作 AMI
  • EC2 micro 採用 EBS 管理,每一次製作 AMI 會產生新的 EBS snapshot,記得定時去清理
  • 程式碼若是透過 git 管理,可以在 /etc/rc.local 上設定開機自動更新方式,至少 AMI 不是最新程式碼時也還能堪用(但 git server 掛了就...)
  • 如果想要透過 web cgi 更新,記得把 source tree 的 owner 或 group (搭配775方式) 設定 www-data,可以用 sudo -u www-data /tmp/update.sh 進行測試
AWS Elastic Load Balancing:
  • 使用 AWS ELB 時,可以讓多台機器綁定在固定的 DNS Name,記得需要處理一下多台機器進行切換其 Session/Cookie 問題,例如單純的使用情境,可以透過設定 Stickiness: LBCookieStickinessPolicy, expirationPeriod='600' 等方式來應付等
  • 對於有使用 HTTP Authentication 的 web server 而言,因為帳密是一直隨 browser 傳給 web server 的(就像 cookie一樣),所以 ELB 再切換機器時,不會因機器不同台而再次詢問帳密
AWS Auto Scaling:
  • 對 Auto Scaling 而言,預設機器都是關機後就下線,除非改掉預設方式,不然更新系統時 reboot 的結果就是開一台新機器,此外,把 Auto Scaling Group 刪掉等同把機器都下線
  • 透過製作新的 AMI 來取代系統更新時,而原先設定好的 Auto Scaling Launch Config 不能更新採用的 AMI,但可透過新增新的 Launch Config 後,更新 Auto Scaling 設定後,再刪掉舊的 Launch config 等