IBM Cloud Kubernetes サービス (通称:IKS)で、Aego Workflow を動作させる場合の注意点について挙げ、これらに対処しながら、Argo workflow を利用する方法について記述した。この情報は、執筆時点の情報であり、Argoプロジェクトは、インキュベーション段階にあるため、プロジェクトの成長と時間経過によって、ここに挙げた問題は解決されることを願う。
問題1 クラウドのPostgreSQLと接続できない
Argo workflow は、ジョブの実行状態を管理するために、PostgreSQL または MySQL を利用することになっている。クイックインストールでは、Kubernetesクラスタ上に PostgreSQL を起動する。しかも、永続ボリュームを使わず、コンテナ上にデータを保存している。そのため、停止と起動を実施すると、過去のデータを失ってしまう。
この問題を解決するために、クラウドのデータベースサービス PostgreSQL を利用したい。クラウドのデータベースサービスは、クラウド内から内部的に接続できるだけでなく、パブリックネットワークにエンドポイントを置いて、クラウド外からも利用できる。そのため、クライアント証明書を使用したTLS暗号化による接続が必須となっている。ところが、Argo workflow の データベース接続のコードでは、TLS証明書の設定が明確になっていない。 以下にソースコード(https://github.com/argoproj/argo/blob/master/persist/sqldb/sqldb.go) を添付した。インポートしているPostgreSQLのライブラリがTLS証明書をサポートしていれば、簡単に機能追加して、プルリクを挙げられそうな気がする。
package sqldb
import (
"fmt"
"time"
log "github.com/sirupsen/logrus"
"k8s.io/client-go/kubernetes"
"upper.io/db.v3/lib/sqlbuilder"
"upper.io/db.v3/mysql"
"upper.io/db.v3/postgresql"
<中略>
func CreatePostGresDBSession(kubectlConfig kubernetes.Interface, namespace string, cfg *config.PostgreSQLConfig, persistPool *config.ConnectionPool) (sqlbuilder.Database, string, error) {
if cfg.TableName == "" {
return nil, "", errors.InternalError("tableName is empty")
}
userNameByte, err := util.GetSecrets(kubectlConfig, namespace, cfg.UsernameSecret.Name, cfg.UsernameSecret.Key)
if err != nil {
return nil, "", err
}
passwordByte, err := util.GetSecrets(kubectlConfig, namespace, cfg.PasswordSecret.Name, cfg.PasswordSecret.Key)
if err != nil {
return nil, "", err
}
var settings = postgresql.ConnectionURL{
User: string(userNameByte),
Password: string(passwordByte),
Host: cfg.Host + ":" + cfg.Port,
Database: cfg.Database,
}
if cfg.SSL {
if cfg.SSLMode != "" {
options := map[string]string{
"sslmode": cfg.SSLMode,
}
settings.Options = options
}
}
session, err := postgresql.Open(settings)
if err != nil {
return nil, "", err
}
<後略>
上記の理由から、現時点ではクラウドのPostgreSQLデータベースのサービスと接続できない。 OperatorHubには、PostgreSQLのオペレータが複数登録されているので、この中から品質の良いものを選択して使用するのが良さそうだ。
問題2 デフォルトのエクゼキューターでは動かない
現在のArgo workflow でのエクゼキューターとしては選択できるのは、Docker, kubelet, Kubernetes API (k8sapi), Porcess Namespace Shring (pns) であり、デフォルトは Docker になっている。しかし、Dockerをコンテナランタイムに使用しているクラウドは、もはや存在しない。残りから評価して選択する必要がある。利点と欠点を、アルゴプロジェクトのドキュメントに挙げてあるので、https://argoproj.github.io/argo/workflow-executors/ 参考にすると良い。
筆者が試したところ、Docker 以外のエクゼキューターに 変更することで問題が発生した。 GitHub の Issue generate artifact via script failed when mounted emptyDir #1667 (https://github.com/argoproj/argo/issues/1667 にも上がっているが、Openを継続している(2020/7/15)
オンプレミスのKubernetesクラスタでは、コンテナランタイムをDockerに切り替えて、Argo workflow を利用することが出来るが、クラウドのKubernetesサービスでは、利用者が自由に変更できないので、問題を許容する必要がある。
Dockerエンジンでも内部的にcontainerdを使用しているので、以下のように、UNIXソケットのアドレス設定を加えることで、コンテンを実行は可能であるが、結果をアウトプットする段階でエラーになる。
apiVersion: v1
kind: ConfigMap
metadata:
name: workflow-controller-configmap
data:
artifactRepository: |
archiveLogs: true
s3:
bucket: my-bucket
<中略>
containerRuntimeExecutor: docker
dockerSockPath: /var/run/containerd/containerd.sock
以下のようにエクゼキューターを k8sapiにすることで、制約はあるものの利用できるようになる。
containerRuntimeExecutor: k8sapi
問題3 オブジェクトストレージのHMAC有効化された認証情報が必須
Argo workflow は、AWS S3、GCS, Minio がサンプルとして挙げられている(https://argoproj.github.io/argo/configure-artifact-repository/) 。もちろん、S3互換API を持つオブジェクト・ストレージへの接続も可能だ。IBM Cloud のオブジェクトストレージを使用する場合、認証情報を生成する際に詳細オプションから、HMAC資格情報(https://cloud.ibm.com/docs/cloud-object-storage/hmac?topic=cloud-object-storage-hmac&locale=ja) を有効化しておく必要がある。これによって、S3互換のアクセスキーとシークレットを生成するので、これをセットする。
以下にICOS(IBM Cloud Object Storage)を使用する際のワークフローコントローラーのコンフィグマップをあげる。
apiVersion: v1
kind: ConfigMap
metadata:
name: workflow-controller-configmap
data:
artifactRepository: |
archiveLogs: true
s3:
endpoint: s3.jp-tok.cloud-object-storage.appdomain.cloud
bucket: argo-workflow-artifacts
accessKeySecret:
name: my-icos-cred
key: accesskey
secretKeySecret:
name: my-icos-cred
key: secretkey
insecure: true
この例では、シークレット my-icos-cred に accesskey と secretkey の設定が必要となる。これは、HMACを有効化することで、cos_hmac_keys が生成される。access_key_id を accesskey、 seceretkey を secret_access_keyにそれぞれセットすることでアクセスできる。
{
"apikey": "0viPHOY7LbLNa9eLftrtHPpTjoGv6hbLD1QalRXikliJ",
"cos_hmac_keys": {
"access_key_id": "347aa3a4b34344f8bc7c7cccdf856e4c",
"secret_access_key": "gvurfb82712ad14e7a7915h763a6i87155d30a1234364f61"
},
"endpoints": "https://control.cloud-object-storage.test.cloud.ibm.com/v2/endpoints",
"iam_apikey_description": "Auto generated apikey during resource-key operation for Instance - crn:v1:bluemix:public:cloud-object-storage:global:a/3ag0e9402tyfd5d29761c3e97696b71n:d6f74k03-6k4f-4a82-b165-697354o63903::",
"iam_apikey_name": "auto-generated-apikey-f9274b63-ef0b-4b4e-a00b-b3bf9023f9dd",
"iam_role_crn": "crn:v1:bluemix:public:iam::::serviceRole:Manager",
"iam_serviceid_crn": "crn:v1:bluemix:public:iam-identity::a/3ag0e9402tyfd5d29761c3e97696b71n::serviceid:ServiceId-540a4a41-7322-4fdd-a9e7-e0cb7ab760f9",
"resource_instance_id": "crn:v1:bluemix:public:cloud-object-storage:global:a/3ag0e9402tyfd5d29761c3e97696b71n:d6f74k03-6k4f-4a82-b165-697354o63903::"
}
解決策 PostgreSQL オペレーターによるデプロイ
オペレーターHub の中から、Postgres-Operator (https://operatorhub.io/operator/postgres-operator)
を利用することにした。
はじめに、デフォルトの名前空間をdefaultにしてから、オペレーターをインストールする。
$ kubectl apply -k github.com/zalando/postgres-operator/manifests
serviceaccount/postgres-operator created
clusterrole.rbac.authorization.k8s.io/postgres-operator created
clusterrole.rbac.authorization.k8s.io/postgres-pod created
clusterrolebinding.rbac.authorization.k8s.io/postgres-operator created
configmap/postgres-operator created
service/postgres-operator created
deployment.apps/postgres-operator created
$ kubectl get pod -l name=postgres-operator
NAME READY STATUS RESTARTS AGE
postgres-operator-85978df8cf-bxsnb 1/1 Running 0 14s
これで、Postgres-Operator の CRD が作成されるので、起動する PostgresSQL の設定を変更したい場合は利用できる。
$ kubectl get crd postgresqls.acid.zalan.do
NAME CREATED AT
postgresqls.acid.zalan.do 2020-07-15T00:11:20Z
$ kubectl get crd postgresqls.acid.zalan.do -o yaml
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
creationTimestamp: "2020-07-15T00:11:20Z"
generation: 1
name: postgresqls.acid.zalan.do
resourceVersion: "3142326"
selfLink: /apis/apiextensions.k8s.io/v1/customresourcedefinitions/postgresqls.acid.zalan.do
uid: b4aedc82-6949-4348-915a-aa35f217f481
<以下省略>
以下のYAMLを適用して、オペレーターに指示を与え、PostgreSQLを起動する。
apiVersion: acid.zalan.do/v1
kind: postgresql
metadata:
name: postgresql-cluster
spec:
databases:
argodb: argo
numberOfInstances: 2
postgresql:
version: '11'
teamId: POSTGRESQL
users:
argodb_user: []
argo:
- superuser
- createdb
volume:
size: 5Gi
patroni:
pg_hba:
- host all all 0.0.0.0/0 md5
このマニフェストを適用するだけで、オペレーターを起動してくれる。だいたい5分くらいで起動する。
$ kubectl apply -f postgresql-cluster.yaml
起動を確認するには次の方法が使える。二つのノードが異なるノードに配置されている。
maho:operator maho$ kubectl get pod -l application=spilo,cluster-name=postgresql-cluster -o wide
NAME READY STATUS RESTARTS AGE IP NODE postgresql-cluster-0 1/1 Running 0 5m38s 172.30.177.214 10.44.12.57
postgresql-cluster-1 1/1 Running 0 3m43s 172.30.195.98 10.44.10.23
オペレータで起動することで、PVCのマニフェストを書くことなく、永続ボリュームの確保も完了している。
$ kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
pgdata-postgresql-cluster-0 Bound pvc-9bf59b8d-75ab-46dc-be1c-8ba71b707f0b 10Gi RWO ibmc-vpc-block-10iops-tier 6m57s
pgdata-postgresql-cluster-1 Bound pvc-7605eb6c-5fb3-4c66-9802-f23772bc48d6 10Gi RWO ibmc-vpc-block-10iops-tier 5m2s
postgresのパスワードは以下の方法で取得できる。
$ export PGPASSWORD=$(kubectl get secret postgres.postgresql-cluster.credentials -o 'jsonpath={.data.password}' | base64 -d)
$ echo $PGPASSWORD
6pTt0mhbA2OnwNTnabEVi3p8HLzwfaGe4qHpYdLM7FN1tzjwXpMEc9JJzwnoCBtf
このパスワードの値をシークレットに設定しておく。
apiVersion: v1
kind: Secret
metadata:
name: argo-postgres-config
labels:
app: argo
stringData:
username: postgres
password: 6pTt0mhbA2OnwNTnabEVi3p8HLzwfaGe4qHpYdLM7FN1tzjwXpMEc9JJzwnoCBtf
type: Opaque
オブジェクトストレージの準備
IBM Cloud Object Storage のインスタンスを設定して、バケット argo-workflow-artifacts を作成して、サービス資格情報を作成する。この時、HMACを設定しておく。
apiVersion: v1
kind: Secret
metadata:
labels:
app: argo
name: my-icos-cred
stringData:
accesskey: 20ae470bb23348f4adwdw4d780d8c8139
secretkey: 0905424439cefa9f5b0b52975484964eee3f4e28617c6b12
type: Opaque
Argo workflow の起動
Argo workflow の マニフェスト を分解して、内容を理解しやすいようにして、GitHub https://github.com/takara9/argo-workflow へ登録した。
$ git clone https://github.com/takara9/argo-workflow
$ cd argo-workflow
ローカルへクローンした後、前述のように作成した postgresql-cred.yaml と icos-cred.yaml を作成する。そして、クラスタへ適用する。
$ kubectl apply -k ./
反対に、削除するときは、kubectl delete -k ./
とすれば良い。次のように表示されるようになったら起動が完了しているので、次へ進む。
$ kubectl get pod
NAME READY STATUS RESTARTS AGE
argo-server-7d7644fbd8-mvfw9 1/1 Running 0 31s
workflow-controller-9ddc4456d-vfkn8 1/1 Running 0 31s
Argo workflow コンソールの起動
ポートフォワードで、ローカルのポート番号にバインドして、アクセスする。
$ kubectl -n argo port-forward deployment/argo-server 2746:2746
これで、http://127.0.0.1:2746 で Argo workflow の画面が表示される。
テスト実行
UPLOAD FILEのアイコンから https://github.com/argoproj/argo/tree/master/examples 中から hello-world.yaml のラベルを修正して実行する。 もちろん、事前にローカルへクローンしておく必要がある。
「+ SUBMIT NEW WORKFLOW」をクリックして、バッチ処理をサブミットする。
橙色いアイコンが表示されるので、クリックすると、ポッドとしてのコンテナの起動状況を確認できる。そして、ジョブが完了したら、出力結果がオブジェクトストレージに保存されていることを確認できる。
ジョブがフェイルするケースも載せておく。次は、dag-contiue-on-fail.yaml の実行例で、各ステップの出力は、アイコンをクリックして、OUTPUT ARTIFACTS から、オブジェクトストレージに保存された内容を表示してみることができる。
実際のバッチジョブを利用する場合には、データの前処理結果などをオブジェクトストレージに保存することになる。出力先がオブジェクトストレージなので、ワークディスクが満杯でジョブネットが異常終了といったことも、起きることは無いだろうし、便利だと思う。
まとめ
Kubernetesでオペレーターを利用して、データベースサービスを使うことに慣れていない点や、PostgreSQLの設定が良く知らないため、手間取ったが、オペレーターを利用してデータベースを使用すると、細かな設定は全てオペレータが代行してくれるので、非常に便利だと感じた。IBM オブジェクトストレージは、もともと Claversave と呼ばれた S3互換に開発されたオブジェクトストレージなので、HMACを指定して認証情報を取得できれば、問題なく繋がる。
アルゴ ワークフローで、ジョブネットが編集できて、YAMLを出力できたら便利なんだろうなぁと思うが、これからの発展に期待したい。 Argo CD と Argo Event についても試してたい。