はじめに
こんにちは、GMOソリューションパートナーのTKです。
当社には、AWSの学習用のアカウントがあります。
今回はそれを活用して、ローカルのdocker環境上で動かしていたSeleniumを
ECSのFargateで動作させてみることにしました。
構成
root
├ python
│ └ Dockerfile
│ └ requirements.txt
│ └ script.py
├ selenium
│ └ Dockerfile
└ docker-compose.yml
コード
pythonディレクトリ配下
1 2 3 4 5 6 7 8 9 10 11 12 13 |
FROM python:3.12 ENV TZ Asia/Tokyo RUN apt-get update && \ apt-get install -y sudo vim && \ apt-get clean COPY ./ /app RUN pip install -r /app/requirements.txt WORKDIR /app CMD ["/bin/bash"] |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
import os from selenium import webdriver from selenium.webdriver.common.by import By driver = webdriver.Remote( command_executor = os.environ["SELENIUM_URL"], options = webdriver.ChromeOptions() ) url = "https://techblog.gmo-ap.jp/" driver.get(url) articles = driver.find_elements(By.TAG_NAME, "article") for article in articles: title = article.find_element(By.TAG_NAME, "h2").text print(title) driver.close() driver.quit() |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
attrs==24.2.0 certifi==2024.8.30 cffi==1.17.1 h11==0.14.0 idna==3.10 outcome==1.3.0.post0 pycparser==2.22 PySocks==1.7.1 selenium==4.25.0 sniffio==1.3.1 sortedcontainers==2.4.0 trio==0.26.2 trio-websocket==0.11.1 typing_extensions==4.12.2 urllib3==2.2.3 websocket-client==1.8.0 wsproto==1.2.0 |
seleniumディレクトリ配下
1 |
FROM selenium/standalone-chrome:latest |
rootディレクトリ直下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
version: '3' services: selenium: build: ./selenium shm_size: 2g ports: - "4444:4444" - "7900:7900" python: build: ./python volumes: - ./python:/app stdin_open: true tty: true depends_on: - selenium environment: SELENIUM_URL: http://selenium:4444/wd/hub |
処理の内容としては、当社のテックブログのトップページにアクセスして
タイトルの要素を取得し、標準出力に出力します。
AWSコンソール
ECRのリポジトリを作成
以下の二つのリポジトリを作成します。
xxxxxxxxxxxx.dkr.ecr.region.amazonaws.com/techblog/python
xxxxxxxxxxxx.dkr.ecr.region.amazonaws.com/techblog/selenium
「プッシュコマンドを表示」を押し、手順に従い、ECRのリポジトリへイメージをプッシュします。
※AWS CLI が必要なので、以下のリンクを参考にインストールして、AWS CLI を利用してください。
https://docs.aws.amazon.com/ja_jp/cli/latest/userguide/getting-started-install.html
ECSのクラスターを作成する
任意の名前でクラスター名を作成します。
ここではtech_blogとします。
ECSのタスク定義を作成する
新しいタスク定義の作成をクリックし、必要な情報を入力します。
- タスク定義の設定
タスク定義ファミリーに任意の名前を入力 - インフラストラクチャの要件の起動タイプ
Fargateを選択 - コンテナ – 1
名前:python
イメージURI:xxxxxxxxxxxx.dkr.ecr.region.amazonaws.com/techblog/python:latest
ポートマッピング:不要なので削除
必須コンテナ:はい
環境変数 – オプション
キー:SELENIUM_URL、値:http://localhost:4444/wd/hub
※ ECSの同一タスク内のコンテナはlocalhostで通信ができます。
▼ Docker 設定 – オプション
コマンド:python,-u,script.py
作業ディレクトリ:/app - コンテナ – 2
名前:selenium
イメージURI:xxxxxxxxxxxx.dkr.ecr.region.amazonaws.com/techblog/selenium:latest
必須コンテナ:はい
ポートマッピング
コンテナポート:4444
プロトコル:TCP
ポート名:selenium-4444-tcp
アプリケーションプロトコルHTTP
▼ HealthCheck – オプション
コマンド:CMD-SHELL, curl -f http://localhost:4444 || exit 1
間隔、タイムアウト、開始期間、再試行 に任意の値を入力 - 再びコンテナ – 1
▼ スタートアップの依存関係の順序 – オプション
コンテナ名:selenium
条件:Healthy
JSONを使用した新しいタスク定義の作成も可能です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 |
{ "family": "tech_blog_task", "containerDefinitions": [ { "name": "python", "image": "account_id.dkr.ecr.ap-northeast-1.amazonaws.com/techblog/python:latest", "cpu": 0, "portMappings": [], "essential": true, "command": [ "python", "-u", "script.py" ], "environment": [ { "name": "SELENIUM_URL", "value": "http://localhost:4444/wd/hub" } ], "environmentFiles": [], "mountPoints": [], "volumesFrom": [], "dependsOn": [ { "containerName": "selenium", "condition": "HEALTHY" } ], "workingDirectory": "/app", "ulimits": [], "logConfiguration": { "logDriver": "awslogs", "options": { "awslogs-group": "/ecs/tech_blog_task", "mode": "non-blocking", "awslogs-create-group": "true", "max-buffer-size": "25m", "awslogs-region": "ap-northeast-1", "awslogs-stream-prefix": "ecs" }, "secretOptions": [] }, "systemControls": [] }, { "name": "selenium", "image": "account_id.dkr.ecr.ap-northeast-1.amazonaws.com/techblog/selenium:latest", "cpu": 0, "portMappings": [ { "name": "selenium-4444-tcp", "containerPort": 4444, "hostPort": 4444, "protocol": "tcp", "appProtocol": "http" } ], "essential": true, "environment": [], "environmentFiles": [], "mountPoints": [], "volumesFrom": [], "logConfiguration": { "logDriver": "awslogs", "options": { "awslogs-group": "/ecs/tech_blog_task", "mode": "non-blocking", "awslogs-create-group": "true", "max-buffer-size": "25m", "awslogs-region": "ap-northeast-1", "awslogs-stream-prefix": "ecs" }, "secretOptions": [] }, "healthCheck": { "command": [ "CMD-SHELL", "curl -f http://localhost:4444 || exit 1" ], "interval": 10, "timeout": 5, "retries": 3, "startPeriod": 30 }, "systemControls": [] } ], "executionRoleArn": "arn:aws:iam::account_id:role/ecsTaskExecutionRole", "networkMode": "awsvpc", "requiresCompatibilities": [ "FARGATE" ], "cpu": "1024", "memory": "3072", "runtimePlatform": { "cpuArchitecture": "X86_64", "operatingSystemFamily": "LINUX" } } |
タスクの実行
新しいタスクの実行をクリックします。
必要な項目を入力します。そのほかはデフォルト設定でOKです。
- 環境
▼ コンピューティング設定 ((アドバンスト))
コンピューティングオプション:起動タイプ
起動タイプ:FARGATE
プラットフォームバージョン:LATEST - デプロイ設定
アプリケーションタイプ:タスク
タスク定義
ファミリー:作成したタスク
リビジョン:実行するタスクのバージョン
必要なタスク:1 - ネットワーキング
VPC、サブネット、セキュリティグループの作成は省略します。
インターネットにアクセスできればOKです。
タスクが作成されたので、タスクをクリックします。
起動するのを少し待ち、ログのタブを開くと、無事実行されていることが確認できました。
気づき、ポイントなど
個人的なポイントや気がついたことについてです。
コンテナ間の通信方法を悩んでいましたが、以下のように対応することで解決できました。
ローカル環境のコンテナ上では、docker-compose.ymlのenvironmentで環境変数を
「http://selenium:4444/wd/hub」 とコンテナ名を指定するのに対して
ECSのタスクでは、タスク定義で環境変数を
「http://localhost:4444/wd/hub」 とlocalhostで指定します。
また、python側の処理が終了した後、seleniumコンテナが起動し続ける必要がないため、タスクを終了させる方法を検討しました。
コンテナ – 1 pythonコンテナの必須コンテナをはいにすると
処理が停止した際に、タスク自体が停止されます。
一方、必須コンテナをいいえにすると
seleniumコンテナは起動しつづけます。
処理が終了した際にタスクを停止したい場合は、必須コンテナをはいにするとよいかもしれません。
その他、AWS SDKのboto3でタスクを停止する処理を入れる方法も考えました。
実行中のタスクのログタブに標準出力が処理完了まで表示されないことに気が付きました。
リアルタイムに表示させるには、Pythonの標準出力のバッファリングを明示的にオフにする必要があります。
python -u script.py のように -u オプションを使用することで
リアルタイムでログを出力させることができます。
おわりに
ローカルのdocker環境上で動かしていたSeleniumを、ECSのFargateでも動作させることができました。
これを機にサーバーレス化を進めたいと思っています。