鳩小屋

落書き帳

GitOps:GitHub Actions+Argo CD+Kubernetes

kurobato.hateblo.jp
前回はDockerfileのリポジトリを更新すると自動ビルドされたコンテナイメージがdocker hubにプッシュされる環境を用意しました。
次は、Argo CDというツールを使って、k8sマニフェストリポジトリを更新すると自動でコンテナがデプロイされる環境を構築します。

argoproj.github.io

環境

実機ホストOS:ubuntu
Kubernetes(minikube+virtualbox)

参考:k8s構築手順
Istio:マイクロサービス基盤入門 - 鳩小屋

Argo CDインストール

argocdネームスペースを作成してminikube k8s上にargoCDをインストールします。

#k8sロードバランサの用意(argocd-serverへアクセス用)
#minikubeで提供されるtunnelを起動します。
$ minikube tunnel
Status:	
	machine: minikube
	pid: 4324
	route: 10.96.0.0/12 -> 192.168.99.100
	minikube: Running
	services: [argocd-server, gitops-service, istio-ingressgateway]

#k8s node確認
$ kubectl get nodes -o wide
NAME           STATUS   ROLES                  AGE    VERSION   INTERNAL-IP      EXTERNAL-IP   OS-IMAGE               KERNEL-VERSION   CONTAINER-RUNTIME
minikube       Ready    control-plane,master   11d    v1.21.2   192.168.99.100           Buildroot 2020.02.12   4.19.182         docker://20.10.6
minikube-m02   Ready                     136m   v1.21.2   192.168.99.101           Buildroot 2020.02.12   4.19.182         docker://20.10.6
minikube-m03   Ready                     135m   v1.21.2   192.168.99.102           Buildroot 2020.02.12   4.19.182         docker://20.10.6

#argocdのインストール
$ kubectl create namespace argocd
$ kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml

ポッドの確認

いろいろポッドがデプロイされています。
argocd-serverがユーザアクセス用のサーバみたいです。

$ kubectl get pod -n argocd
NAME                                  READY   STATUS    RESTARTS   AGE
argocd-application-controller-0       1/1     Running   0          10m
argocd-dex-server-68c7bf5fdd-flk9c    1/1     Running   0          10m
argocd-redis-7547547c4f-pcmlk         1/1     Running   0          10m
argocd-repo-server-58f87478b8-lhg78   1/1     Running   0          10m
argocd-server-6f4fcdc5dc-bpmgc        1/1     Running   0          10m

サービスの確認

argocd-serverはデフォルトでは外部公開されていません。
argocd-serverに接続できるようにサービスタイプをLoadBalancerに変更します。

$ kubectl patch svc argocd-server -n argocd -p '{"spec": {"type": "LoadBalancer"}}'
$ kubectl get services -n argocd
NAME                    TYPE           CLUSTER-IP      EXTERNAL-IP    PORT(S)                      AGE
argocd-dex-server       ClusterIP      10.98.145.53             5556/TCP,5557/TCP,5558/TCP   3h5m
argocd-metrics          ClusterIP      10.110.187.11            8082/TCP                     3h5m
argocd-redis            ClusterIP      10.96.238.136            6379/TCP                     3h5m
argocd-repo-server      ClusterIP      10.107.230.1             8081/TCP,8084/TCP            3h5m
argocd-server           LoadBalancer   10.107.71.74    10.107.71.74   80:30364/TCP,443:32221/TCP   3h5m
argocd-server-metrics   ClusterIP      10.107.149.9             8083/TCP                     3h5m

これでargocd-serverサービス経由でargocd-serverポッドにアクセスできます。

CLIのダウンロード

sudo curl -sSL -o /usr/local/bin/argocd https://github.com/argoproj/argo-cd/releases/latest/download/argocd-linux-amd64
sudo chmod +x /usr/local/bin/argocd

argocd-server へのアクセス

10.107.71.74:80にアクセスします。

f:id:FallenPigeon:20210821094108p:plain

初期パスワードの確認(初期ユーザはadmin)

$ kubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath="{.data.password}" | base64 -d && echo
XXnTZyfLjyehwude

リポジトリ登録

f:id:FallenPigeon:20210821105042p:plain

k8sマニフェストのあるリポジトリをargocdに登録します。

f:id:FallenPigeon:20210821104641p:plain

k8sにデプロイするサンプルアプリとサービス

Hello GitOps!を返すwebサーバコンテナをk8sのdeployment podとしてデプロイし、ロードバランサで公開します。

package main

import (
  "fmt"
  "net/http"
)

func handler(w http.ResponseWriter, r *http.Request){
  fmt.Fprintf(w,"Hello GitOps!!")
}

func main(){
  http.HandleFunc("/",handler)
  http.ListenAndServe(":8080",nil)
}
# Stage-1
FROM golang:1.16 as builder
COPY ./app/main.go ./
RUN go build -o /gitops-go-app ./main.go

# Satge-2
FROM ubuntu
EXPOSE 8080
COPY --from=builder /gitops-go-app /.
ENTRYPOINT ["./gitops-go-app"]
apiVersion: apps/v1
kind: Deployment
metadata:
  name: gitops-deployment
spec:
  replicas: 3
  selector:
    matchLabels:
      app: gitops
  template:
    metadata:
      labels:
        app: gitops
    spec:
      containers:
      - name: gitops
        image: docker.io/l3j5g7d9/gitops-go-app:latest
        imagePullPolicy: IfNotPresent
apiVersion: v1
kind: Service
metadata:
  name: gitops-service
spec:
  type: LoadBalancer
  ports:
    - name: gitops
      protocol: TCP
      port: 80
      targetPort: 8080
  selector:
    app: gitops

f:id:FallenPigeon:20210821104711p:plain

マニフェストが認識されました。

手動同期

設定が手動同期になっているため、同期ボタンをポチります。
f:id:FallenPigeon:20210821105145p:plain

すると、deployment podが展開されたような表示になります。
f:id:FallenPigeon:20210821105158p:plain

k8s上で動いているか確認します。

$ kubectl get pods -n default
NAME                                 READY   STATUS    RESTARTS   AGE
gitops-deployment-64879cfb89-n48hc   2/2     Running   0          21m
gitops-deployment-64879cfb89-q878s   2/2     Running   0          21m
gitops-deployment-64879cfb89-v58gk   2/2     Running   0          21m

$ kubectl get services -n default
NAME             TYPE           CLUSTER-IP      EXTERNAL-IP     PORT(S)        AGE
gitops-service   LoadBalancer   10.106.186.67   10.106.186.67   80:32018/TCP   2m45s
kubernetes       ClusterIP      10.96.0.1                 443/TCP        11d

$ curl 10.106.186.67:80
Hello GitOps!!

ポッドとサービスが作成され、ロードバランサ経由でwebサーバにアクセスできています。

自動同期

次は自動同期を有効化します。これでk8sリポジトリを変更すると自動検出してk8sにデプロイしてくれるようになるっぽいです。
f:id:FallenPigeon:20210821111202p:plain

リポジトリk8sマニフェストのポッド名を変更してみます。
f:id:FallenPigeon:20210821111732p:plain

すると、argoとk8s上で変更内容が自動反映されました。

f:id:FallenPigeon:20210821112103p:plain

$ kubectl get pods -n default
NAME                                         READY   STATUS    RESTARTS   AGE
gitops-deployment-changed-64879cfb89-q2gnj   2/2     Running   0          5m21s
gitops-deployment-changed-64879cfb89-rk6cn   2/2     Running   0          5m21s
gitops-deployment-changed-64879cfb89-trjn9   2/2     Running   0          5m21s

$ kubectl get services -n default
NAME             TYPE           CLUSTER-IP      EXTERNAL-IP     PORT(S)        AGE
gitops-service   LoadBalancer   10.106.186.67   10.106.186.67   80:32018/TCP   43m
kubernetes       ClusterIP      10.96.0.1                 443/TCP        11d

$ curl 10.106.186.67:80
Hello GitOps!!

これにてCD完成です。GUIポチポチできるので楽ちんですね。

CICDパイプラインの作成

ここまでGithub Actions(CI)とArgo CD(CD)の環境を構築してきました。
最後にこれらを統合してCICDパイプラインにします。
つまり、アプリケーションのソースコードとDockerfileをリポジトリにプッシュするとビルドからデプロイまで全自動で行われるようにします。

1.Github Actions:Dockerfileのリポジトリ更新をトリガとして自動ビルド+コンテナイメージのプッシュ
2.Argo CD:k8sマニフェストリポジトリ更新をトリガとして自動デプロイ

方法としては、Github Actionsの処理でk8sマニフェストリポジトリを更新する処理を加えることでArgo CDの同期処理が起動するようにします。
処理としてはapp.yamlのポッド名をgitops-deployment[デプロイ番号]に書き換えてプッシュするだけです。
実際には、Helmも組み込んでKubernetesマニフェストを管理するのが無難っぽいのですが、少しややこしくなるので今回は省きます。

余談ですがyqコマンドなんてあったんですね。最近インフラレイヤはyamlだらけなので重宝しそうです。

code/.github/workflows$ nano main.yml 
name: Github Action CI

on:
  push:
    branches: [ main ]

jobs:
  build:
    name: GitOps Workflow
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v2
      #Buildkitによるイメージビルド
      - name: Build an image from Dockerfile
        run: |
          DOCKER_BUILDKIT=1 docker image build . -f app/Dockerfile --tag ${{ secrets.DOCKERUSER }}/gitops-go-app:latest
      #Trivyによるイメージスキャン  
      - name: Run Trivy
        uses: aquasecurity/trivy-action@master
        with:
          image-ref: '${{ secrets.DOCKERUSER }}/gitops-go-app:latest'
          format: 'table'
          exit-code: '1'
          ignore-unfixed: true
          severity: 'CRITICAL,HIGH'
      #DockerHubにイメージプッシュ
      - name: Push Image
        run: |
          docker login docker.io --username ${{ secrets.DOCKERUSER }} --password ${{ secrets.DOCKERPASSWORD }}
          docker image push ${{ secrets.DOCKERUSER }}/gitops-go-app:latest
      #Kubernetesマニフェストの更新
      - name: Change Pod Name
        run: |
          echo -e "machine github.com\nlogin ${{ secrets.GITHUBUSER }}\npassword ${{ secrets.GITHUBTOKEN }}" > ~/.netrc
          git config --global user.email ${{ secrets.EMAIL }}
          git config --global user.name ${{ secrets.GITHUBUSER }}
          git clone https://github.com/${{ secrets.GITHUBUSER }}/config.git
          cd config/manifest
          yq e '.metadata.name = "gitops-deployment${{ github.run_number }}"' -i app.yaml
          git add app.yaml
          git commit -m ${{ github.run_number }} -a
          git push origin main

Github Actionsのワークフロー完了後にリポジトリのapp.yamlを確認するとポッドラベルが更新されています。

f:id:FallenPigeon:20210821170355p:plain

f:id:FallenPigeon:20210821170410p:plain

次にArgo CDのコンソールを確認すると、更新されたポッド名が反映されています。

f:id:FallenPigeon:20210821170834p:plain

k8sでもちゃんと動いています。

$ kubectl get pods -n default
NAME                                   READY   STATUS    RESTARTS   AGE
gitops-deployment31-64879cfb89-6ds4x   2/2     Running   0          24m
gitops-deployment31-64879cfb89-hfhh2   2/2     Running   0          24m
gitops-deployment31-64879cfb89-tmlf9   2/2     Running   0          24m

$ kubectl get services -n default
NAME             TYPE           CLUSTER-IP      EXTERNAL-IP     PORT(S)        AGE
gitops-service   LoadBalancer   10.106.186.67   10.106.186.67   80:32018/TCP   6h31m
kubernetes       ClusterIP      10.96.0.1                 443/TCP        12d

$ curl 10.106.186.67:80
Hello GitOps!!

これでCICDパイプラインの完成です。
とりあえず動いたので満足。