1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

【強化学習】クラウドサービスを利用した分散強化学習(kubernetes編)

Last updated at Posted at 2023-11-25

この記事は自作している強化学習フレームワークの解説記事です。

この記事のコード場所:examples/kubernetes

無料編の続きです。

1:【強化学習】クラウドサービスを利用した分散強化学習(無料編)
2:ここ
3:【強化学習】クラウドサービスを利用した分散強化学習(GKE/有料編)

アーキテクチャ等の概要は前の記事を見てください。
まずはローカルでkubernetesを作成してみます。

全体イメージ

今回作成したい構成は複数のサービスが入り混じっています。
こういった複数のサーバを一元管理する方法の1つに kubernetes(k8s) があります。
k8sは今回初めて触るのである程度は大目に見てください…。

k8sの概念を簡単に言うと、Podが1つの仮想的なPCで、Podの中に複数のDockerコンテナがあるイメージです。
今回作成する構成の全体イメージは以下です。

a-ページ7.drawio.png

大きく3つのPodを作成します。

  1. RedisPod
    • 全体で1Pod
    • RedisサーバコンテナとRedis-commander(GUI用)の2つのコンテナが起動
    • TaskBoard/ParameterBoard/Queueの3つの役割を兼任
    • 外部/内部を通してアクセスできるのはこのPodに対してのみを想定
  2. TrainerPod
    • 全体で1Pod
    • Trainer用のコンテナが1つ動く想定
  3. ActorPod
    • 全体でN個のPodが起動
    • 1Podに対してActor用のコンテナが1つ動く想定

ActorですがPodをレプリカという機能で複製します。(正しい使い方か分かりませんが…)
各PodはServiceという概念でそれぞれ接続します。(図でいう矢印)
※Pod内のコンテナはlocalhost扱いでそのまま通信できます
※Redisの各役割は別Podに切り出し可能です。最初はQueueの役割を別PodにRabbitMQを使っていましたが煩雑だったので統合しました。

DockerDesktop + kubernetes

k8s は DockerDesktop が公式で機能を提供しているので勉強もかねてこれを利用します。

DockerDesktopがない場合は以下からインストールします。
https://www.docker.com/products/docker-desktop/

k8sの有効化は以下です。
https://matsuand.github.io/docs.docker.jp.onthefly/desktop/kubernetes/

詳細は省略しますが、有効にすれば立ち上がってきます。
k8sはコンテキストという単位でプロジェクトを扱っているようで、docker-desktopというものが出来てるはずです。

"kubectl" コマンドが使えるかの確認も兼ねて、以下でコンテキストを確認しておきます。

# コンテキスト変更
> kubectl config use-context docker-desktop
> kubectl config get-contexts
CURRENT   NAME            CLUSTER         AUTHINFO       NAMESPACE
*         docker-desktop  docker-desktop  docker-desktop

Podの概念

ここが結構悩んだので備忘録で書いておきます。
k8sには Pod,ReplicaSet,Deployment という概念があり、以下の意味になります。

  • Podはk8sの最小単位で1つのPodに複数のコンテナが入り、Pod内は同じリソースが共有される(CPU,メモリやネットワーク等が共通)
  • ReplicaSetは同じPodを複製する機能
  • DeploymentはReplicaSetをラップし、デプロイ・更新・ロールバック等も含めて管理する

包含関係としては、Deployment ⊃ ReplicaSet ⊃ Pod となります。

基本はDeploymentを使えば問題ないですが、今回のバッチ処理みたいな要件だと相性が悪いようで、次の記事のGKEでリソースの確保で失敗するわするわ…。
Deploymentは少々高機能かもしれないのでPodを基本利用します。

一般的にDeploymentを使わずにPodを使う場合は以下のようです。(ChatGPT曰く)

  • 学習やテスト:Deploymentは複数のPodを管理するためのもので、単一のPodを使う場合は直接Podを定義することがあります
  • 一時的なジョブやタスク:例えばバッチ処理やデータのクリーニングなどの一時的な作業を行う場合は、そのジョブ専用のPodを直接作成することがあります
  • 特定の制御が必要な場合:Deploymentはデプロイや更新の管理に便利なツールですが、特定の制御が必要な場合はPodを直接作成する場合があります

ほぼ今回の場合ですね。
私の解釈ですが、永続的なサービスはDeployment、バッチ処理系はPod(or ReplicaSet)で扱った方が安定するイメージです。

1. Redisの立ち上げ

まずはWeb上のコンテナイメージをそのまま使えるRedisを立ち上げてみます。
マニフェストは以下です。
GEA側もあるので作業ディレクトリは分けたいと思います。
ディレクトリ構成は以下です。

./
 └ docker_desktop/ # new
    └ redis.yaml   # new
./docker_desktop/redis.yaml
apiVersion: v1
kind: Service
metadata:
  name: redis-internal-service
spec:
  type: ClusterIP
  selector:
    app: redis
  ports:
  - name: redis-internal-port
    protocol: TCP
    port: 6379
    targetPort: 6379

---
apiVersion: v1
kind: Service
metadata:
  name: redis-external-service
spec:
  type: NodePort
  selector:
    app: redis
  ports:
  - name: redis-external-port
    protocol: TCP
    port: 6379
    nodePort: 30001
  - name: redis-web-port
    protocol: TCP
    port: 8081
    nodePort: 30002


---
apiVersion: v1
kind: ConfigMap
metadata:
  name: redis-config
data:
  redis.conf: |-
    bind 0.0.0.0
    port 6379

---
apiVersion: v1
kind: Pod
metadata:
  name: redis-pod
  labels:
    app: redis
spec:
  containers:
    - name: redis-server
      image: redis:7.2-alpine
      ports:
        - containerPort: 6379
      command: ["redis-server", "/etc/redis/redis.conf"]
      volumeMounts:
        - name: redis-config
          mountPath: /etc/redis
      resources:
        limits:
          cpu: "900m"
    - name: redis-commander
      image: rediscommander/redis-commander:latest
      env: [{"name": "REDIS_HOSTS", "value": "local:localhost:6379"}]
      ports:
        - containerPort: 8081
      resources:
        limits:
          cpu: "200m"
          memory: "64Mi"
  volumes:
    - name: redis-config
      configMap:
        name: redis-config

内容はRedisのPodを作成し、クラスタ内部に6379ポートを、外部に向けて30001ポート(Redis)、30002ポート(Redis管理画面)を開放する内容になっています。

ファイルを作成したらプロンプトで以下を実行し、起動させます。

> kubectl apply -f ./docker_desktop/redis.yaml

以下で確認できます。

> kubectl get pods
NAME        READY   STATUS    RESTARTS   AGE
redis-pod   2/2     Running   0          14s

起動ができていれば以下から管理画面に入ることができます。

削除する場合は以下です。

> kubectl delete -f ./docker_desktop/redis.yaml

2. コンテナイメージの作成

Actor/Trainerで使用するコンテナイメージを作成します。
このイメージはGKEでも同じものを使います。
ここで作成するファイルは以下です。

./
 ├ docker_desktop/
 |  └ redis.yaml
 |
 ├ dockerfile        # new
 ├ server_actor.py   # new
 └ server_trainer.py # new

まずはエントリーポイント用のコードを作成します。

./server_actor.py
from srl.runner.distribution import RedisParameters, actor_run_forever
from srl.utils.common import logger_print

logger_print()
actor_run_forever(RedisParameters(host="redis-internal-service"), None)
./server_trainer.py
from srl.runner.distribution import RedisParameters, trainer_run_forever
from srl.utils.common import logger_print

logger_print()
trainer_run_forever(RedisParameters(host="redis-internal-service"),None)

k8s内部からのアクセスになるので "redis-internal-service" でアクセスします。
(これはredis.yamlで定義しています)

次にDockerfileを作成します。
Tensorflowのイメージはサイズが大きいです…

./dockerfile
# syntax=docker/dockerfile:1

# --- select image CPU(1.76GB) or GPU(7.38GB)
FROM tensorflow/tensorflow:2.14.0-gpu
#FROM tensorflow/tensorflow:2.14.0

WORKDIR /code
RUN apt-get update \
 && apt install -y --no-install-recommends git \
 && apt-get install -y --no-install-recommends libgl1-mesa-dev libglib2.0-0 \
 && rm -rf /var/lib/apt/lists/* \
 && pip install --no-cache-dir git+https://github.com/pocokhc/simple_distributed_rl@v0.13.3 \
# 必要なライブラリを適宜入れる
 && pip install --no-cache-dir opencv-python pygame gymnasium redis async_timeout

# エントリーポイントのファイルをコピー
COPY server_*.py /code/

以下でイメージ化します。(最後の.も必要)

> docker build --pull --rm -t srl_qiita:latest .

3. Actor/Trainerマニフェストファイル

最後にActor/Trainer用のマニフェストファイルを作成します。

./
 ├ docker_desktop/
 |  ├ redis.yaml
 |  ├ actor.yaml    # new
 |  └ trainer.yaml  # new
 |
 ├ dockerfile
 ├ server_actor.py
 └ server_trainer.py
./docker_desktop/trainer.yaml
apiVersion: v1
kind: Pod
metadata:
  name: trainer-pod
spec:
  containers:
    - name: trainer-node
      image: srl_qiita:latest
      imagePullPolicy: Never  # この行でローカルのimageを見るようにしている
      #command: ["sh", "-c", "while true; do sleep 3600; done"]
      command: ["python", "-u", "/code/server_trainer.py"]
      resources:
        limits:
          cpu: "950m"

※DockerDesktopのk8sはGPUには対応していないとの事
※pythonは"-u"オプションを追加し、printがk8sのlogに出力されるようにしています

./docker_desktop/actor.yaml
apiVersion: apps/v1
kind: ReplicaSet
metadata:
  name: actor-pods
spec:
  replicas: 1  # ここがそのままActor数になる
  selector:
    matchLabels:
      app: actor
  template:
    metadata:
      labels:
        app: actor
    spec:
      containers:
        - name: actor-node
          image: srl_qiita:latest
          imagePullPolicy: Never  # この行でローカルのimageを見るようにしている
          #command: ["sh", "-c", "while true; do sleep 3600; done"]
          command: ["python", "-u", "/code/server_actor.py"]
          resources:
            limits:
              cpu: "950m"

ActorはReplicaSetで作成しています。
replicasの数が複製するPodの数を表し、そのままActor数になります。
ただ、その数だけCPUも必要になりますが…。

以下で起動します。

> kubectl apply -f ./docker_desktop/trainer.yaml
> kubectl apply -f ./docker_desktop/actor.yaml

以下で確認します。

> kubectl get pods
NAME               READY   STATUS    RESTARTS   AGE
actor-pods-2h47z   1/1     Running   0          61s
redis-pod          2/2     Running   0          19m
trainer-pod        1/1     Running   0          13s

もし起動しない場合は command をデバッグ用に変えて起動し、以下で中に入って調べてみてください。
([NAME]には get pods の NAME を入力)

> kubectl exec -it [NAME] -- bash

また、各Podのログを見るのは以下です。
-fはオプションを追加するとリアルタイムでログが見れます。

> kubectl logs -f [NAME]

4. 学習の実行

これで準備ができたので学習します。
Redisのポート30001に向けてアクセスします。

main.py
import srl
from srl.algorithms import dqn
from srl.runner.distribution import RedisParameters

rl_config = dqn.Config()
rl_config.hidden_block.set_mlp((64, 64))
rl_config.memory.set_proportional_memory()
rl_config.memory.capacity = 5000
runner = srl.Runner("Pendulum-v1", rl_config)

runner.train_distribution(
    RedisParameters(host="localhost", port=30001),
    actor_num=1,
    max_train_count=20_000,
)

print(runner.evaluate())

実行結果

> python main.py
18:45:35 ACTIVE  1.00s(     - left),      0tr (-1451.4eval)
 trainer  573db05d-d1e4-4178-a2c0-b36f03fbf303  0.9s:     0tr/s,    0recv/s,        tr,       0recv
 actor0   not assigned
18:46:36 ACTIVE   1.0m(  3.5m left),   4487tr (-1244.9eval)
 trainer  573db05d-d1e4-4178-a2c0-b36f03fbf303 10.2s:    74tr/s,  440recv/s,    4487tr,   26578recv
 actor0   b456bf97-deda-4839-8a83-fb26977abe18 10.2s:   372st/s,  371send/s,   22461st,   22424send
18:47:36 ACTIVE   2.0m(  1.7m left),  10291tr (-0.968eval)
 trainer  573db05d-d1e4-4178-a2c0-b36f03fbf303  9.5s:    96tr/s,  455recv/s,   10291tr,   54000recv
 actor0   b456bf97-deda-4839-8a83-fb26977abe18  9.5s:   457st/s,  457send/s,   50006st,   49987send
18:48:36 ACTIVE   3.0m(43.33s left),  15941tr (-127.4eval)
 trainer  573db05d-d1e4-4178-a2c0-b36f03fbf303  9.8s:    93tr/s,  444recv/s,   15941tr,   80832recv
 actor0   b456bf97-deda-4839-8a83-fb26977abe18  9.8s:   446st/s,  447send/s,   76966st,   76950send
18:49:11 END   3.6m( 0.00s left),  20000tr ( -13.4eval)
 trainer  573db05d-d1e4-4178-a2c0-b36f03fbf303  1.0s:   115tr/s,  436recv/s,   20000tr,   96217recv
 actor0   b456bf97-deda-4839-8a83-fb26977abe18  5.0s:   509st/s,  509send/s,   94930st,   94915send

[-13.179330630227923, -302.80894811078906, -12.675792848691344, -128.10729961842299, -247.43346573412418, -12.799946028739214, -365.9429765045643, -132.10430204682052, -14.009112168103456, -134.4337202552706]

終わったら削除しておきます。
この構成はDBへの保存は全くないので、削除すればすべて初期化されます。

# 一括削除
> kubectl delete -f ./docker_desktop

# 一括起動
> kubectl apply -f ./docker_desktop

次はこのk8s環境を Google Kubernetes Engine (GKE) に作成してみたいと思います。
【強化学習】クラウドサービスを利用した分散強化学習(GKE/有料編)

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?