nikkie-ftnextの日記

イベントレポートや読書メモを発信

イベントレポート | #pyhack にてArgoを触りました(MLOpsに使っていきたいなと素振り🏏しました)

はじめに

頑張れば、何かがあるって、信じてる。nikkieです。
MLOpsにやや興味があり、最近はGitHub ActionsでMLOpsという以下のブログ記事を真似しています。

ここで登場したArgoなるものが気になり、9月の Python mini Hack-a-thon で触ってみました。

目次

勉強会の概要

(第115回)Python mini Hack-a-thon(オンライン) - connpass

スプリントのゆるい版みたいな感じで各自自分でやりたいことを持ってきて、勝手に開発を進めています。

Twitterの #pyhack ハッシュタグを追うと雰囲気がつかめると思います。
集中してもくもくできて素振りが捗りました。
1日お疲れさまでした。ありがとうございました!

Argoとは

Container-native Workflow Engineを標榜するArgo。
ワークフローの各ジョブはKubernetesk8s)上で動かせます。
今日触った範囲では、コンテナをつなげてワークフローを定義し実行できるという理解です。
ワークフローの各ジョブはコンテナ化されているので、異なるプログラミング言語で書けるというのが1つのメリットなのかなと思いました(マイクロサービスっぽい!)。

k8sクラスタAWSGCPに立てるのは素振りにはちょっとお高いので、minikubeを使ってローカルで動かしました。

取り組んだこと

  • ローカル環境構築
  • Argoのexampleで概念を掴む
  • GitHub Actionsで動かしているCIワークフローをArgoで動かしてみる

先日書いたGitHub ActionsでMLOpsの記事でやっているスクリプト実行がArgoでできました!

ローカル環境構築

minikube

minikube start | minikube の手順でv1.12.3をインストール。
過去にbrewで入れたv1.9系は動かなくなっていたので、アンインストールして新しいものにしました1

minikubeコマンド自分用メモ

$ minikube start
$ minikube pause
$ minikube unpause  # pauseした後の再開
$ minikube stop

Argo

minikube startしてある状態で以下を進めます。
Quick Start - Argo Workflows - The workflow engine for Kubernetes

kubectl port-forwardすると、ブラウザからArgoを設定できます。
(こちらで操作もできるようです2が、今回はyamlを書いて進めていきました)

f:id:nikkie-ftnext:20200926203657p:plain

Argo CLI

ArgoのQuick startで案内されているCLIも入れます。
Release v2.11.0 · argoproj/argo · GitHub をもとにmacOSにインストールしました。

Argo CLIkubectlのラッパーというのがポイントでした。
argoコマンドもkubectl同様-nnamespaceが指定できます。
これを指定しないと、~/.kube/configの指定によりminikube namespaceを操作するので、ワークフローがうまく実行されませんでした。

Argoのexamplesで概念を掴む

examplesのREADMEで手厚く解説されています。

READMEの一部を写経

READMEの上から

  • Hello World!(docker/whalesayを動かす)
  • Parameters(docker/whalesayに引数を渡す)
  • Steps(順次実行、並列実行)
  • Artifacts(あるコンテナの出力を次のコンテナの入力へ)

を進めました。

Argoはk8sのカスタムリソースとして定義されているので、k8sと同様にyamlファイルを書けば済みます。
k8sは業務で触っているので、kind: Workflowを指定したyamlファイルを書くというのは、結構しっくり来ました。
どんなパラメタで、また、どうファイルマウントしてDockerイメージを動かすかyamlで書いていく感じです。

argoコマンド チートシート

※自分用まとめです

  • argo lintyamlを書いた直後に、yamlの形式に異常がないか確認するのに使います
  • argo list:ワークフローの一覧
  • argo submityamlをもとにワークフローを作成して実行(--watchを付けて実行状況を監視できます3
  • argo get:指定したワークフローの詳細が見られます
  • argo logs:指定したワークフロー全体や各ステップ(ポッド)のログが見られます
  • argo delete:指定したワークフローの削除

行き着いたコマンド順(-n argoはnamespaceの指定です)

$ argo -n argo lint argo-workflow/practice.yaml
$ argo -n argo list
$ argo -n argo submit argo-workflow/practice.yaml --watch

STEP                       TEMPLATE          PODNAME                            DURATION  MESSAGE
 ✔ artifact-passing-v6rnm  artifact-example
 ├---✔ generate-artifact   whalesay          artifact-passing-v6rnm-1548704093  5s
 └---✔ consume-artifact    print-message     artifact-passing-v6rnm-1648314195  7s

# ワークフロー全体のログを見る
$ argo -n argo logs artifact-passing-v6rnm

# ワークフローの中の generate-artifact ステップだけのログを見る
$ argo -n argo logs artifact-passing-v6rnm artifact-passing-v6rnm-1548704093

$ argo -n argo delete artifact-passing-v6rnm

GitHub Actionsで動かしているCIワークフローをArgoで動かしてみる

以下のリポジトリGitHub Actions製ワークフロー(Pythonのunittestを実行)を
bring-script-more-users/python-unittest.yml at master · ftnext/bring-script-more-users · GitHub Argoで動かしてみます。
参考にしたのはこちら。

パブリックリポジトリにて動作

以下の2ステップで実装しました4

  1. checkout:git cloneしてソースコード取得5
  2. test-unit:依存をインストールしてからユニットテストを実行
apiVersion: argoproj.io/v1alpha1
kind: Workflow
metadata:
  generateName: bringscript-ci-
spec:
  entrypoint: bringscript-ci  # templatesの中のbringscript-ciを実行する指定
  arguments:  # 引数をデフォルト値込みで設定
    parameters:
    - name: repo
      value: https://github.com/ftnext/bring-script-more-users.git
    - name: revision
      value: bringscript0.1.0

  templates:
  - name: bringscript-ci
    steps:  # 順次実行する書き方
    - - name: checkout
        template: checkout
    - - name: test-unit
        template: test-unit
        arguments:
          artifacts:  # 取得したソースコードのディレクトリをバインド
          - name: source
            from: "{{steps.checkout.outputs.artifacts.source}}"

  - name: checkout
    inputs:
      artifacts:
      - name: source
        path: /src
        git:
          repo: "{{workflow.parameters.repo}}"
          revision: "{{workflow.parameters.revision}}"
    outputs:
      artifacts:
      - name: source
        path: /src
    container:
      image: python:3.8
      command: ["/bin/sh", "-c"]
      args: ["cd /src && git status && ls -l"]

  - name: test-unit
    inputs:
      artifacts:
      - name: source
        path: /python/src/github.com/ftnext/bring-script-more-users
    container:
      image: python:3.8-slim-buster
      command: ["/bin/sh", "-c"]
      args: ["
        cd /python/src/github.com/ftnext/bring-script-more-users/bringscript &&
        python -m pip install .[dev] &&
        pytest
      "]

checkoutステップはgit pullできればいいので、Dockerイメージはpython:3.8を指定。
test-unitステップはpythonだけあればいいので、python:3.8-slim-busterを指定しています。

プライベートリポジトリの場合は?

k8sSecretsを作って、プライベートリポジトリにアクセスするための情報をワークフローから参照します。

kubectl -n argo create secret generic github-creds --from-file=ssh-private-key=<SSH秘密鍵のパス>

ssh-private-keyの部分がSecretsのキーになります。
指定していないとファイル名がキーになるという挙動でちょっとハマりました6

# ... snip ...
  - name: checkout
    inputs:
      artifacts:
      - name: source
        path: /src
        git:
          repo: "{{workflow.parameters.repo}}"
          revision: "{{workflow.parameters.revision}}"
          sshPrivateKeySecret:  # 追加
            name: github-creds  # 追加
            key: ssh-private-key  # 追加
    outputs:
      artifacts:
      - name: source
        path: /src
    container:
      image: python:3.8
      command: ["/bin/sh", "-c"]
      args: ["cd /src && git status && ls -l"]
# ... snip ...

f:id:nikkie-ftnext:20200926203642p:plain

GitHub ActionsとArgoの違い

ステップの分け方が違うことに気づきました。

GitHub Actionsは1つの環境(ランナー)で複数のステップを実行します。
そのため、依存のインストールとユニットテストの実行を別ステップに分けることができます

Argoはさまざまな環境(コンテナ)でステップを実行していきます。
そのため、依存のインストールとユニットテストの実行を分けずにPythonが使えるコンテナの中に押し込むことになりました。

次にやること

今回Argoを触り、例のGitHub ActionsでMLOpsブログの真似は先に進めそうです。
AWSGCPなどクラウド上にクラスタを作り、Argoをセットアップ、そしてGitHub ActionsからArgoを呼び出すというのを今後取り組んでみようと思います。

おまけ:Dockerまわりのコンマリ

minikubeの再設定まわりでつまづき、「もしかしてDockerのDisk image size?」と見てみました(これは見当違いでした)。
macOSのDockerは定期的にコンマリする(片付けする)しかないんですね。
Docker向けに割り当てたDiskが減るたびに暫定対応として割り当てを増やしていたのですが、Docker.rawが巨大になり、ディスクを圧迫していました。

Disk image sizeを広げていくのはアンチパターンだと考えます。
上限に達したら、sizeを減らすしかなくなり、そのときにDockerイメージが全部消えます。
大量のDockerイメージの中から消してはいけないものがないか確認するよりも、定期的にコンマリしたほうがいいなと今回体験しました。


  1. ~/.minikube~/.kube/configに残っていた古い情報のためにminikube startに失敗したようなので、これらの削除も必要でした。

  2. Private Repositories - Argo CD - Declarative GitOps CD for Kubernetes など(Argo CDというのは今回触ったArgoとは別物なのかな?)

  3. kubectl-wと違ってwatchsubmitサブコマンドだけみたいです

  4. spec.entrypointに指定したtemplateが実行されます。このtemplateの中でcheckout→test-unitと順次実行する実装です(プログラミングの関数っぽいですね)

  5. artifactのgitの指定についてはドキュメントのこのあたりにありました

  6. SSH秘密鍵パスフレーズが指定されていたためにエラーになったので、Secretsに設定する際はssh-keygenで再設定して外しました。 ref: sshの秘密鍵のパスフレーズを削除する - Qiita