ArgoでCIパイプラインを構築する

# 実現したいこと
argo workflowを使ってCIパイプラインを構築する
今回はリポジトリのクローンまで
対象はGithubプライベートリポジトリ(内容は[前回](https://qiita.com/Static_Novice/items/39362f445be66474b596)と同じ)

# 環境
- Golang 1.13.5
- Docker 18
- AWS 東京リージョン

argoはAWS EKSクラスターに配置

# やったこと
### Argoの導入
基本的には [Argo公式ドキュメント](https://argoproj.github.io/docs/argo/readme.html)に沿って進める

前提としてEKSにCI用クラスタ `ci-cluster` を作成しておく

```bash
# namespaceの作成
kubectl create ns argo

# install.yamlをダウンロードしてargo namespaceにapply
kubectl apply -n argo -f https://raw.githubusercontent.com/argoproj/argo/v2.2.1/manifests/install.yaml

# default namespaceのサービスアカウントにadmin権限を付与
kubectl create rolebinding default-admin --clusterrole=admin --serviceaccount=default:default
```

***
### とりあえずexampleを動かす
Argoはyamlでパイプラインを記述する
例として公式exampleの[conflip.yaml](https://github.com/argoproj/argo/blob/master/examples/coinflip.yaml)を実行する

*coinflip.yaml*

```yaml

# The coinflip example combines the use of a script result,
# along with conditionals, to take a dynamic path in the
# workflow. In this example, depending on the result of the
# first step, 'flip-coin', the template will either run the
# 'heads' step or the 'tails' step.
apiVersion: argoproj.io/v1alpha1
kind: Workflow
metadata:
  generateName: coinflip-
spec:
# entorypointで指定されたtemplates.nameが起動時に実行される
  entrypoint: cinflip
  templates:
  # ここからパイプラインの構成
  - name: coinflip
    steps:
    # name: タスクの名前
    - - name: flip-coin
        # 実際のタスクの処理への参照
        template: flip-coin
    # - - name: stage-name で直列のタスクになる
    - - name: heads
        template: heads
        when: "{{steps.flip-coin.outputs.result}} == heads"
    # - name: stage-name で直前のタスクと並列のタスクになる
      - name: tails
        template: tails
        when: "{{steps.flip-coin.outputs.result}} == tails"
  # パイプラインの構成ここまで


  #　タスクの中身の定義
  - name: flip-coin
    # タスク毎にPodが立ち上がるのでそれぞれのタスクで起動するイメージを指定する
    script:
      image: python:alpine3.6
      # タスク内で実行するコマンドの言語指定 今回はpython
      command: [python]
　　　 # 実際に実行させる処理
      source: |
        import random
        result = "heads" if random.randint(0,1) == 0 else "tails"
        print(result)

  - name: heads
    container:
      image: alpine:3.6
      # タスク内で実行するコマンドの言語指定 今回はshell
      command: [sh, -c]
      args: ["echo \"it was heads\""]

  - name: tails
    container:
      image: alpine:3.6
      command: [sh, -c]
      args: ["echo \"it was tails\""]
```

パイプラインの実行は`argo submit`で行う  

```bash
argo submit coinflip.yaml

Name:                coinflip-pwk7p
Namespace:           argo
ServiceAccount:      default
Status:              Pending
Created:             Thu Jan 23 09:52:11 +0900 (1 second from now)
```

これで argo namespace上にタスク毎のPodが立ち上がりそれぞれ順に実行される
今回であれば作成されるPodは3つ
作成されるPod名は以下の通り 
`${metadata.generateName}{パイプライン毎のプレフィクス}-{タスク毎のプレフィクス}`
今回であれば例えば`coinflip-pwk7p-4162510633`

***
**UIツールでパイプラインを監視する**

argoにはUIツールが備わっているので，パイプラインの構成が複雑になる場合などに有用である．
前段でinstall.yamlを使ってargoの起動をした場合は，既に`argo-ui-{プレフィクス}`という名前のPodが立てられているはず．
以下のコマンドでUIツールを`localhost:8001`にポートフォワードする

```bash
kubectl -n argo port-forward deployment/argo-ui 8001:8001
```

これでブラウザでパイプラインの実行を確認できるようになった．

***
**UIツールをインターネットから確認する**

localhostから確認しているUIツールをインターネット経由で確認できるようにする
これで，確認のために毎回ポートフォワードする必要がなくなる

argo-ui serviceのTypeを ClusterIPからLoadBalancerに変更する

argoを導入した際のinstall.yamlを編集する

```yaml
.
.
.
apiVersion: v1
kind: Service
metadata:
  name: argo-ui
  namespace: argo
# TypeをLoadBalancerに変更
spec:
  type: LoadBalancer
  # アクセス元IPアドレスを制限したい場合は記述
  loadBalancerSourceRanges:
    - "ALLOWED ACCESS IP"
  ports:
  - port: 80
    targetPort: 8001
  selector:
    app: argo-ui
.
.
.
```

これを再度 applyすればLoadBalancerのエンドポイントからargo-uiにアクセスできる


### Githubから

大筋は公式exampleの[input-artifact-git.yaml](https://github.com/argoproj/argo/blob/master/examples/input-artifact-git.yaml)

```yaml
apiVersion: argoproj.io/v1alpha1
kind: Workflow
metadata:
  generateName: input-artifact-git-
spec:
  entrypoint: git-clone
  templates:
  - name: git-clone
    inputs:
      artifacts:
      - name: argo-source
        path: /src
        git:
          repo: https://github.com/argoproj/argo.git
          revision: "v2.1.1"
    container:
      image: golang:1.10
      command: [sh, -c]
      args: ["git status && ls && cat VERSION"]
      workingDir: /src
```

このパイプラインは`git-clone`タスク１つのみの単純なもので
`git-clone`も実行しているコマンドは `git status && ls && cat VERSION"` のみ
ただ，このタスクの実行前にgitリポジトリのリソースを入力Artifactとして`/src`配下に配置している

今回は `myOrg/myRepo` リポジトリのmasterブランチをクローンしたいので以下のようになる

```yaml
apiVersion: argoproj.io/v1alpha1
kind: Workflow
metadata:
  generateName: input-artifact-git-
spec:
  entrypoint: git-clone
  templates:
  - name: git-clone
    inputs:
      artifacts:
      - name: argo-source
        # リポジトリの中身を配置するディレクトリ
        path: /src
        # gitリポジトリ指定
        git:
          repo: https://github.com/myOrg/myRepo.git
          revision: "master"
    container:
      image: golang:1.10
      command: [sh, -c]
      args: ["git status && ls && cat VERSION"]
      workingDir: /src
```


***

**プライベートリポジトリからクローンする**

前段の方法ではパブリックリポジトリしかクローンできない
プライベートリポジトリからクローンする場合にはgitの認証情報を登録する必要がある

再びinput-artifact-git.yamlを参考にすると下記のようになる

```yaml
apiVersion: argoproj.io/v1alpha1
kind: Workflow
metadata:
  generateName: input-artifact-git-
spec:
  entrypoint: git-clone
  templates:
  - name: git-clone
    inputs:
      artifacts:
      - name: argo-source
        # リポジトリの中身を配置するディレクトリ
        path: /src
        # gitリポジトリ指定
        git:
          repo: https://github.com/myOrg/myRepo.git
          revision: "master"
          # gitの認証情報を指定
          # kubernetes Secretから github-creds.usernameを取得する
          usernameSecret:
            name: github-creds
            key: username
          # kubernetes Secretから github-creds.passwordを取得する
          passwordSecret:
            name: github-creds
            key: password

    container:
      image: golang:1.10
      command: [sh, -c]
      args: ["git status && ls && cat REDOME.md"]
      workingDir: /src
```

パイプラインの定義ファイルに直接認証情報を書くのではなくKubernetesのSecretsに格納された認証情報を呼び出す
今回は github-credsに usernameとpasswordという名前で登録しておく


***
**動かす**

```bash
argo submit input-artifact-git.yaml
```

ちゃんとクローンできた


# 完成
とりあえずgitプライベートリポジトリからクローンができるところまで完成
この後testだったりbuildだったりを記述していく
ここから先は他のciツールと大差ない(と思っている)

つまづいたところは，

- Secret内の認証情報をタスク内で参照するところ
- admin権限を付与するサービスアカウントを間違えていたこと
- タスク内でechoやprintした内容が各podのログに出力されていたこと

各タスクのログはタスクを実行するPodに出力されるのでエラーが起きた際には，
どのタスク内でパイプラインが止まったのかを見て，そのPodの中に入るといった手順が必要
[stern](https://github.com/wercker/stern) などのPod横断で監視できるツールを導入するのが良さそう

### そのあと
gitクローンだけではCIとはとても呼べないので，このあとはクローンしたリソースを使ってtest/build/imageプッシュをしていく

また，手動でパイプラインを動かしているのでgithubのwebhookを使って，masterブランチのpushをトリガーにパイプラインの実行ができるように`argo events`を導入する