【增强学习】利用云服务进行分布式增强学习(Kubernetes 编辑)

本文是一篇关于自制强化学习框架的解说文章。

 

这篇文章的代码位置:examples/kubernetes

免费章节的延续。

1:使用云服务进行分布式强化学习(免费版)
2:这里
3:使用云服务进行分布式强化学习(GKE/付费版)

请参考前篇文章了解有关架构等的概述。
首先我们在本地创建一个kubernetes。

整体印象

这次想构建的组合包含了多个服务。
对于像这样集成多个服务器的方法之一,有 Kubernetes(k8s)。
因为是第一次接触k8s,请宽容一些…。

简单来说,k8s的概念是指一个Pod是一个虚拟的计算机,而Pod内包含多个Docker容器。下面是我们即将创建的整体架构图。

a-ページ7.drawio.png

创建三个大型Pod。

    1. RedisPod

RedisPod是一个Pod,里面包含了一个Redis服务器容器和一个Redis-commander(用于GUI界面)容器。它兼任了TaskBoard、ParameterBoard和Queue三个角色的功能。只有通过外部或内部的访问可以连接到这个Pod。

TrainerPod

TrainerPod是一个Pod,里面只有一个Trainer用的容器运行。

ActorPod

一共有N个ActorPods启动,每个ActorPod里面只有一个Actor用的容器运行。

作为一个Actor,我会使用Pod的复制功能来复制。(我不确定这是正确的用法)每个Pod都会通过Service的概念进行连接。(在图中表示为箭头)
* Pod内的容器可以直接使用localhost进行通信。
* 可以将Redis的不同角色分割到不同的Pod中。最初将队列的角色分割到另一个Pod中并使用RabbitMQ,但这太复杂了,所以我们进行了整合。

Docker桌面版 + Kubernetes

由于DockerDesktop在官方提供了K8s功能,所以我将利用它来学习K8s。

如果没有安装Docker Desktop,则可以从以下链接进行安装:

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(或者ReplicaSet),這樣能保持更穩定的印象。

1. 启动Redis

首先,我们尝试启动可以直接使用Web上的容器镜像的Redis。
下面是指南。
由于GEA还存在,所以我想要分开工作目录。
目录结构如下。

./
 └ docker_desktop/ # new
    └ redis.yaml   # new
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

首先,我们将创建一个用于入口点的代码。

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)
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镜像的尺寸很大……

# 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. 演员/训练师清单文件

最后,将为演员/训练师创建一个清单文件。

./
 ├ docker_desktop/
 |  ├ redis.yaml
 |  ├ actor.yaml    # new
 |  └ trainer.yaml  # new
 |
 ├ dockerfile
 ├ server_actor.py
 └ server_trainer.py
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。
※我们通过添加”-u”选项来使python的print输出到k8s的日志中。

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资源…。

我将在下面启动。(Wǒ .)

> 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

如果无法启动,请将命令更改为调试模式并启动,然后尝试进入内部进行调查(在下面输入pods的名称)。

> kubectl exec -it [NAME] -- bash

另外,查看每个Pod的日志请使用以下方法。
使用-f选项可以实时查看日志。

> kubectl logs -f [NAME]

4. 学习的执行 de

準备就绪后将开始学习。
将会连接到 Redis 的端口 30001。

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]

当完成后我会删除。
这个结构完全没有保存到数据库,所以删除后将会重新初始化。

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

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

我接下来想要在 Google Kubernetes Engine(GKE)上创建这个k8s环境。
【强化学习】使用云服务进行分布式强化学习(GKE/付费版本)

广告
将在 10 秒后关闭
bannerAds