【机器学习学习笔记】思考机器学习在基础设施方面能做的事情

首先

总结一下我为了获得AWS机器学习专家认证所学到的内容。
我会结合实践,对机器学习的构建进行描述。
为了减少构建过程中的工作时间,我努力进行自动化。

机器学习的过程

首先,我們將總結機器學習的主要任務。在這篇文章中,我們將特別關注基礎設施相關的內容。

– 标注
调整数据,以便在推理和学习等任务中更方便地使用。
为数据添加标签,用于分类模型。
存储和处理大量数据。

考虑开发模型并编写代码。
准备编写代码所需的工具。
为执行工具准备资源。

使用大量数据进行学习。
通过重复学习来提高。
对资源进行调整优化。

将已经训练好的模型转换为用于推理的模型。

使用已经训练好的模型进行部署和应用。

在机器学习中,我认为准备数据并将模型训练到目标精度非常重要。因此,我想不会想在其他事情上费心。因此,我会考虑在基础设施方面是否可以实现自动化和节省时间。

※仅提供一种中文翻译选项:

※ 参考

 

思考基础设施能够实现的事情。

在建立方面进行实践。

自动化以前使用IaC手动完成的任务。

我会思考如何建立服务器。虽然不想花费太多时间,但是由于需要按照现有环境来进行,我认为这会很困难。因此,我希望借鉴经验,并将已经完成的事情自动化,使得操作更加轻松。这次我将进行以下事项。

・使用CloudFormation创建服务器。
・从shell文件构建k8s。
・使用k8s清单文件创建容器注册表。

使用CloudFormation创建服务器。

接下来,我们将进行以下实践。
虽然CloudFormation的基本原理与过去相同,但我们增加了EC2实例的容量。这是因为未来我们将使用更大的容器镜像。

 

Resources:
  ec2:
    Type: 'AWS::EC2::Instance'
    Properties:
      InstanceType: t2.medium
      KeyName: <key-name>
      ImageId: ami-02892a4ea9bfa2192
      NetworkInterfaces:
        - AssociatePublicIpAddress: 'true'
          DeviceIndex: '0'
          SubnetId: !Ref <public-subnet>
          GroupSet:
            - !Ref <security-group>
      BlockDeviceMappings:
        - DeviceName: "/dev/xvda"
          Ebs: 
            VolumeSize: 20
            VolumeType: "gp2"
      Tags:
        - Key: Name
          Value: !Sub '${AWS::StackName}-ec2'

使用shell脚本进行k8s的构建。

我会在EC2上创建一个k8s环境。由于在之前的文章中已经介绍过,所以我们将采用相同的方法。如果是第一次创建k8s环境,您需要查找官方页面等来进行错误处理。在本文中,我们将以shell文件的形式重新使用之前文章中的内容。

 

从k8s的清单文件创建容器注册表。

我们将通过后述的方式推送图像以创建POD。为此,我们将创建一个容器注册表。虽然有很多方法可供选择,但这次我们将在本地创建。如果本地有k8s环境,可以使用容器快速准备。有对应的容器镜像可供使用,只需使用它即可。这次我们将它作为POD创建。

 

先创建一个适用于POD的命名空间。

kind: Namespace
apiVersion: v1
metadata:
  name: myns
kubectl apply -f namespace.yaml

POD的yaml配置如下:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: registry
  namespace: myns
spec:
  replicas: 1
  selector:
    matchLabels:
      app: registry
  template:
    metadata:
      labels:
        app: registry
    spec:
      containers:
      - name: registry
        image: docker.io/registry:2.8.1
        volumeMounts:
        - name: registry-config
          mountPath: /etc/docker/registry
        - name: registry-storage
          mountPath: /var/lib/registry
      - name: registry-ui
        image: docker.io/joxit/docker-registry-ui:2.2.1
        envFrom:
        - configMapRef:
            name: registry-ui-config
      volumes:
        - name: registry-config
          configMap:
            name: registry-config
        - name: registry-storage
          hostPath:
            path: /home/ec2-user/registry
            type: DirectoryOrCreate

以下将详细介绍有关yaml配置的内容。

・货运集装箱

コンテナ名メモregistryコンテナレジストリregistry-uiブラウザからコンテナイメージを確認できるようにする

・音量

ボリューム名マウントするコンテナメモregistry-configregistryコンテナレジストリの設定ファイルを格納するregistry-storageregistryコンテナイメージのローカルの保存先

使用ConfigMap来配置容器注册表。以下是ConfigMap的yaml文件。

apiVersion: v1
kind: ConfigMap
metadata:
  name: registry-config
  namespace: myns
data:
  config.yml: |
    version: 0.1
    log:
      fields:
        service: registry
    storage:
      cache:
        blobdescriptor: inmemory
      filesystem:
        rootdirectory: /var/lib/registry
    http:
      addr: :5000
      headers:
        X-Content-Type-Options: [nosniff]
        Access-Control-Allow-Origin: ['*']
        Access-Control-Allow-Headers: ['*']
    health:
      storagedriver:
        enabled: true
        interval: 10s
        threshold: 3
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: registry-ui-config
  namespace: myns
data:
  REGISTRY_URL: http://localhost:30500
  REGISTRY_TITLE: "local registry"

URL和端口将在后面总结。
使用创建的yaml文件创建POD。
创建后,尝试从浏览器访问容器注册表。

image.png

因为还没有推送容器镜像,所以没有显示任何内容。如果已经推送并且注册表的 POD 发生故障,Deployment 会重新启动 POD,并加载本地保存的镜像。

使用Jupyter容器映像来节省安装的麻烦。

为了编写代码,我们首先要准备Jupyter。由于容器映像与容器注册表一样是公开的,我们可以使用它。
接下来,我们要创建一个用于运行Jupyter的POD的yaml文件。只需在Deployment中指定Jupyter映像即可完成。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: jupyter
  namespace: myns
spec:
  replicas: 1
  selector:
    matchLabels:
      app: jupyter
  template:
    metadata:
      labels:
        app: jupyter
    spec:
      containers:
      - name: jupyter
        image: docker.io/jupyter/base-notebook:python-3.10.4

我会创建一个 POD 并尝试从浏览器进行访问。
要首次访问 Jupyter,您需要一个令牌,可以从启动日志中确认。

$ JUPYTER_POD=`kubectl get pod -n myns | grep jupyter | awk '{print $1}'`
$ kubectl logs -n myns $JUPYTER_POD
・・・
    To access the server, open this file in a browser:
        file:///home/jovyan/.local/share/jupyter/runtime/jpserver-7-open.html
    Or copy and paste one of these URLs:
        http://jupyter-7f5b78d4cf-qjbhb:8888/lab?token=<token>
     or http://127.0.0.1:8888/lab?token=<token>
・・・
image.png

我能够访问。
我是第一次使用。可以执行脚本和使用Markdown呢。

image.png

※仅需一个翻译选项,以下是参考翻译:
请提供以下内容的中文本地化翻译。
※参考

 

为了对数据进行整理,准备使用Fluentd。

我们要考虑如何准备学习和推理所需的数据。我们考虑对数据进行以下处理:
· 整形:将数据转换为可处理的格式。
· 分类:对数据进行加工并打上标签。
· 过滤:剔除不需要的数据。

因为有Fluentd这种方法来实现这些,所以我会尝试制作。另外,由于创建Fluentd的配置文件很困难,为了减轻负担,我也会尝试使用Fluentd的图形用户界面功能。

首先,创建容器镜像。
执行以下命令。

#!/bin/bash -ex

REGISTRY_HOST='localhost:30500'

echo 'install git'
sudo yum install git -y

echo 'build fluentd-ui'
COMMIT_ID='d48d672ebc505b7a2ac4ff35afdbb8f843a62ef3'
FLUENTD_UI_VERSION='v1.2.2'
LOCAL_REPO_DIR="$HOME/fluentd-ui"
git clone https://github.com/fluent/fluentd-ui.git $LOCAL_REPO_DIR
cd $LOCAL_REPO_DIR
git checkout $COMMIT_ID
IMAGE=$REGISTRY_HOST/fluent/fluentd-ui:$FLUENTD_UI_VERSION
sudo docker build -t $IMAGE $LOCAL_REPO_DIR
sudo docker push $IMAGE

以下是正在进行的工作:
– 安装git
– 克隆包含用于构建的源文件的git仓库
– 构建并推送fluentd-ui

由于容器注册表使用Service并使用端口30500,因此在构建和推送时需要进行相应的指定。详细信息将在后续说明。

创建一个使用push的镜像来创建Fluentd的POD的yaml文件。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: fluentd
  namespace: myns
spec:
  replicas: 1
  selector:
    matchLabels:
      app: fluentd
  template:
    metadata:
      labels:
        app: fluentd
    spec:
      containers:
      - name: fluentd
        image: localhost:30500/fluent/fluentd-ui:v1.2.2

POD创建后,我们将尝试通过浏览器进行访问。
初始登录信息如下:
– 账户名:admin
– 密码:changeme

 

image.png

我能够访问。
从浏览器上看,似乎可以编辑设置文件和查看日志。

image.png

还可以将Fluentd、Elasticsearch和Kibana结合起来,以便进行数据图表和搜索等操作。

 

准备使用Jenkins来管理处理的进展。

想象一下学习的时候,可能需要使用大量数据进行长时间的处理。如果能够自动化这个过程,我认为开发者的负担将会减轻。
在Jenkins中,您可以实现以下功能:
•将一段代码整理为工作任务,可以定期反复执行。
•拥有GitHub插件,可以更方便地管理源代码。
•具备通知功能,可以更容易地发现问题。

因为Jenkins也有容器镜像可供使用,所以我会尝试一下。
我将创建一个用于创建Jenkins的POD的yaml文件。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: jenkins
  namespace: myns
spec:
  replicas: 1
  selector:
    matchLabels:
      app: jenkins
  template:
    metadata:
      labels:
        app: jenkins
    spec:
      containers:
      - name: jenkins
        image: docker.io/jenkins/jenkins:jdk11
image.png

初次登录密码好像在/var/jenkins_home/secrets/initialAdminPassword中。

$ JENKINS_POD=`kubectl get pod -n myns | grep jenkins | awk '{print $1}'`
$ kubectl exec -n myns $JENKINS_POD -- cat /var/jenkins_home/secrets/initialAdminPassword

登录后,可以安装各种插件,包括GitHub。

image.png

您可以通过“Jenkins管理”->“系统设置”来设置邮件作为通知功能。

image.png

我试着发送了一封电子邮件,收到了以下的邮件。

image.png

为了直观地确认资源情况,准备使用Grafana。

在学习的过程中,可以想象到处理的数据量增加和处理的情况变得更为复杂。这可能会导致服务器的CPU和内存不足。在处理被中断之前,我们希望采取措施,如增加资源或减少处理量等来应对这种情况。因此,我们准备使用Grafana作为监控资源的方法。在Grafana中,我们可以做到以下几点:
– 对指标进行可视化展示。
– 对指标进行警报。
由于使用Operator,安装Grafana会更加容易,因此让我们试试看。

执行以下命令。

#!/bin/bash -ex
SCRIPT_DIR=$(cd $(dirname $0); pwd)

echo 'install helm'
HELM_VERSION='v3.8.1'
cd $HOME
curl "https://get.helm.sh/helm-${HELM_VERSION}-linux-amd64.tar.gz" -o helm.tar.gz
tar -zxvf helm.tar.gz
mv linux-amd64 helm
echo 'export PATH=$PATH:$HOME/helm' >> $HOME/.bashrc
source .bashrc
helm version

echo 'install prometheus operator'
cd $SCRIPT_DIR
PROMETHEUS_OPERATOR_VERSION='36.0.0'
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm install kube-prometheus-stack prometheus-community/kube-prometheus-stack \
--version $PROMETHEUS_OPERATOR_VERSION \
--create-namespace \
-n monitoring \
-f prometheus/prometheus-operator-values.yaml
grafana:
  service:
    type: NodePort
    port: 3000
    targetPort: 3000
    nodePort: 30300

prometheus:
  service:
    type: NodePort
    port: 9090
    targetPort: 9090
    nodePort: 30090

正在进行的工作如下:
– 安装Helm
– 安装Prometheus Operator

另外,在helm安装时使用了yaml文件,目的是将Grafana的Service类型设置为NodePort。具体细节将在后面说明。
接下来,让我们实际尝试访问一下。

image.png

初始用户的用户名和密码已经在k8s环境的Secret中记录。

$ kubectl describe secret -n monitoring kube-prometheus-stack-grafana
Name:         kube-prometheus-stack-grafana
Namespace:    monitoring
Labels:       app.kubernetes.io/instance=kube-prometheus-stack
              app.kubernetes.io/managed-by=Helm
              app.kubernetes.io/name=grafana
              app.kubernetes.io/version=8.5.3
              helm.sh/chart=grafana-6.29.6
Annotations:  meta.helm.sh/release-name: kube-prometheus-stack
              meta.helm.sh/release-namespace: monitoring

Type:  Opaque

Data
====
admin-password:  13 bytes
admin-user:      5 bytes
ldap-toml:       0 bytes
$ kubectl -n monitoring get secret kube-prometheus-stack-grafana -o jsonpath="{.data.admin-user}" | base64 --decode
*** #username
$ kubectl -n monitoring get secret kube-prometheus-stack-grafana -o jsonpath="{.data.admin-password}" | base64 --decode
*** #Password

在默认情况下,仪表板已经创建好了,您可以查看之前创建的POD所使用的内存和CPU的情况。

localhost_3000_d_8b7a8b326d7a6f1f04244066368c67af_kubernetes-networking-namespace-pods_orgId=1&refresh=10s&var-datasource=default&var-cluster=&var-namespace=All&var-resolut.png

此外,您可以创建联系点和警报规则,以便使用警报功能。可以指定警报接收方为电子邮件、Microsoft Teams等。

image.png

此外还可以使用Prometheus。

localhost_9090_graph_g0.expr=node_namespace_pod_container%3Acontainer_cpu_usage_seconds_total%3Asum_irate&g0.tab=0&g0.stacked=0&g0.show_exemplars=0&g0.range_input=15m&g0.end_input=2022-06-11%2007.png

从本地电脑的浏览器访问POD的方法【参考】

总结

这次,我们通过本地PC的浏览器访问了创建的POD。具体方法是通过SSH端口转发和Service的NodePort相结合。首先,我们将POD内部容器正在等待连接的端口分配给节点的端口。我们假设节点能够通过本地PC进行SSH连接,而其他端口是空闲的情况。因此,通过SSH端口转发,我们可以访问容器分配的端口。以下是从浏览器访问容器镜像的路径示意图。

image.png

本地PC通过SSH连接到EC2,但是服务通过registry-ui容器在端口80上进行访问。
响应将在本地PC的端口80上进行监听。可以通过浏览器的URL http://localhost:80 来访问。

用NodePort进行端口分配

根据使用的容器端口,在Service中需要设置NodePort。以下是节点和分配的容器端口的总结。

ノードのポート番号例割り当てるポート番号メモ30500registryコンテナ5000ポートコンテナイメージのpushのため30080registryコンテナ80ポートブラウザからアクセス30001jupyterコンテナ8888ポートブラウザからアクセス30002jenkinsコンテナ8080ポートブラウザからアクセス30003fluentdコンテナ9292ポートブラウザからアクセス30300grafanaコンテナ3000ポートブラウザからアクセス30090prometheusコンテナ9090ポートブラウザからアクセス

此外,还提供了一个示例的容器注册表服务的YAML文件。

apiVersion: v1
kind: Service
metadata:
  name: registry-service
  namespace: myns
spec:
  type: NodePort
  ports:
    - name: "registry"
      protocol: "TCP"
      port: 25000
      targetPort: 5000
      nodePort: 30500
    - name: "registry-ui"
      protocol: "TCP"
      port: 20080
      targetPort: 80
      nodePort: 30080
  selector:
    app: registry

SSH端口转发方法

使用分配的节点端口号,进行SSH端口转发。
通过使用Teraterm的功能,可以省略SSH连接的设置,更加便捷。

image.png

在连接Teraterm的情况下输入所需的信息。
您可以使用一个Teraterm来设置多个端口转发。本地端口对应于在浏览器中输入的端口。
如果每次都很麻烦设置,您还可以将Teraterm的当前设置保存到INI文件中,并在启动时加载它。

image.png

我现在要开始学习。

我还有很多机器学习方面尚未学习的内容。以下是我希望今后学习的内容:
– Spark 分布式处理架构
– AWS SageMaker
– 学习性能评估
– 模型参数调优

最后

关于我提到的功能,我认为AWS也可以实现。AWS提供了自己开发的中间件和托管服务。最近感觉AWS非常强大,我希望学会熟练使用它。