# ArgoCD GitOps В данном практическом занятии рассмотрим возможности [argocd][] в качестве GitOps инструмента. ## Vagrant Для работы будем использовать следующий `Vagrantfile`: ```ruby Vagrant.configure("2") do |config| config.vm.define "argocd" do |c| c.vm.provider "virtualbox" do |v| v.cpus = 2 v.memory = 4096 end c.vm.box = "ubuntu/lunar64" c.vm.hostname = "argocd" c.vm.network "forwarded_port", guest: 8888, host: 8888 c.vm.provision "shell", inline: <<-SHELL apt-get update -q apt-get install -yq docker.io docker-compose-v2 usermod -a -G docker vagrant echo '{"registry-mirrors":["https:\\/\\/mirror.gcr.io"]}' > /etc/docker/daemon.json systemctl restart docker curl -LO https://dl.k8s.io/release/v1.30.0/bin/linux/amd64/kubectl curl -Lo ./kind https://kind.sigs.k8s.io/dl/v0.22.0/kind-linux-amd64 curl -Lo ./argocd https://github.com/argoproj/argo-cd/releases/download/v2.11.2/argocd-linux-amd64 curl -L https://get.helm.sh/helm-v3.15.1-linux-amd64.tar.gz | tar xvzf - --strip-components 1 linux-amd64/helm install -m 755 kubectl kind argocd helm /usr/local/bin/ rm ./* SHELL end end ``` Данная конфигурация установит на виртуальную машину [docker][], [kubectl][], [kind][], [argocd][] и [helm][]. ## Install Для развертывания кластера с помощью [kind][] с возможностью использования локального registry воспользуемся [скриптом][kind-registry]: ```bash #!/bin/sh set -o errexit # 1. Create registry container unless it already exists reg_name='kind-registry' reg_port='5000' if [ "$(docker inspect -f '{{.State.Running}}' "${reg_name}" 2>/dev/null || true)" != 'true' ]; then docker run \ -d --restart=always -p "127.0.0.1:${reg_port}:5000" --network bridge --name "${reg_name}" \ registry:2 fi # 2. Create kind cluster with containerd registry config dir enabled # TODO: kind will eventually enable this by default and this patch will # be unnecessary. # # See: # https://github.com/kubernetes-sigs/kind/issues/2875 # https://github.com/containerd/containerd/blob/main/docs/cri/config.md#registry-configuration # See: https://github.com/containerd/containerd/blob/main/docs/hosts.md cat <main.go package main import ( "net/http" ) func main() { http.ListenAndServe(":8080", http.HandlerFunc( func(w http.ResponseWriter, _ *http.Request) { w.WriteHeader(http.StatusOK) w.Write([]byte("hello\n")) })) } EOF $ cat <Dockerfile FROM mirror.gcr.io/golang:1.21 as build WORKDIR /src COPY main.go /src/ RUN CGO_ENABLED=0 go build -o /bin/app ./main.go FROM scratch COPY --from=build /bin/app /app CMD ["/app"] EOF ``` После чего соберем и запушим в локальный реджестри `registry.traefik.me:5000`: ```console $ docker build -t registry.traefik.me:5000/app:v1 . Sending build context to Docker daemon 45.57kB Step 1/7 : FROM mirror.gcr.io/golang:1.21 as build ---> 0a5922bb6e20 Step 2/7 : WORKDIR /src ---> Running in e76b21970668 Removing intermediate container e76b21970668 ---> 137ecccbe70b Step 3/7 : COPY main.go /src/ ---> e9da6a17b049 Step 4/7 : RUN CGO_ENABLED=0 go build -o /bin/app ./main.go ---> Running in 73edb1a2166d Removing intermediate container 73edb1a2166d ---> 67701db7cb58 Step 5/7 : FROM scratch ---> Step 6/7 : COPY --from=build /bin/app /app ---> 69ba82a41903 Step 7/7 : CMD ["/app"] ---> Running in b1ff3579ccbe Removing intermediate container b1ff3579ccbe ---> c8c5d0269885 Successfully built c8c5d0269885 Successfully tagged registry.traefik.me:5000/app:v1 $ docker push registry.traefik.me:5000/app:v1 The push refers to repository [registry.traefik.me:5000/app] fda33218fe64: Pushed v1: digest: sha256:b6ec73fecc842f529e590bae6c4b6b176d0f4add5997c8e9aa46bd2e5f398466 size: 528 ``` ## Create Chart Создадим также `helm` чарт в репозитории: ```console $ helm create chart Creating chart $ rm -r chart/templates/tests ``` И отредактируем `chart/values.yaml`: ```yaml replicaCount: 1 image: repository: registry.traefik.me:5000/app pullPolicy: IfNotPresent tag: "v1" serviceAccount: create: false autoscaling: enabled: false service: type: ClusterIP port: 8080 ingress: enabled: true hosts: - host: app.traefik.me paths: - path: / pathType: Prefix ``` После чего отправим в удаленный репозиторий: ```console $ git add . $ git commit -m init [main (root-commit) 469db78] init 13 files changed, 363 insertions(+) create mode 100644 Dockerfile create mode 100644 chart/.helmignore create mode 100644 chart/.values.yaml.swp create mode 100644 chart/Chart.yaml create mode 100644 chart/templates/NOTES.txt create mode 100644 chart/templates/_helpers.tpl create mode 100644 chart/templates/deployment.yaml create mode 100644 chart/templates/hpa.yaml create mode 100644 chart/templates/ingress.yaml create mode 100644 chart/templates/service.yaml create mode 100644 chart/templates/serviceaccount.yaml create mode 100644 chart/values.yaml create mode 100644 main.go $ git push Enumerating objects: 17, done. Counting objects: 100% (17/17), done. Delta compression using up to 2 threads Compressing objects: 100% (17/17), done. Writing objects: 100% (17/17), 5.76 KiB | 1.44 MiB/s, done. Total 17 (delta 1), reused 0 (delta 0), pack-reused 0 remote: . Processing 1 references remote: Processed 1 references in total To http://git.traefik.me:8888/alex/app.git * [new branch] main -> main ``` ![](img/argo-gitops4.png) ## Create Argo App Создадим приложение в [argocd][], указав внутренний git адрес: ```bash $ kubectl create -f - < 0a5922bb6e20 Step 2/7 : WORKDIR /src ---> Using cache ---> 137ecccbe70b Step 3/7 : COPY main.go /src/ ---> e8d44019610a Step 4/7 : RUN CGO_ENABLED=0 go build -o /bin/app ./main.go ---> Running in 4e680dcb9a7c Removing intermediate container 4e680dcb9a7c ---> 6a8312d3000d Step 5/7 : FROM scratch ---> Step 6/7 : COPY --from=build /bin/app /app ---> de7646146ed1 Step 7/7 : CMD ["/app"] ---> Running in b9f6a2586cba Removing intermediate container b9f6a2586cba ---> fbd62922d125 Successfully built fbd62922d125 Successfully tagged registry.traefik.me:5000/app:v2 $ docker push registry.traefik.me:5000/app:v2 The push refers to repository [registry.traefik.me:5000/app] f9ea5c2db5d8: Pushed v2: digest: sha256:d33a410af15be7f43416ff61ee3cb2aa947297aeaadfef3a043973ba6caaa0fc size: 528 ``` А в файле `chart/values.yaml` укажем новую версию: ```yaml replicaCount: 1 image: repository: registry.traefik.me:5000/app pullPolicy: IfNotPresent tag: "v2" serviceAccount: create: false autoscaling: enabled: false service: type: ClusterIP port: 8080 ingress: enabled: true hosts: - host: app.traefik.me paths: - path: / pathType: Prefix ``` После чего отправим изменения в удаленный репозиторий: ```console $ git add . $ git commit -m 'update version' [main 7fca3bf] update version 3 files changed, 2 insertions(+), 2 deletions(-) delete mode 100644 chart/.values.yaml.swp $ git push Enumerating objects: 9, done. Counting objects: 100% (9/9), done. Delta compression using up to 2 threads Compressing objects: 100% (5/5), done. Writing objects: 100% (5/5), 446 bytes | 446.00 KiB/s, done. Total 5 (delta 3), reused 0 (delta 0), pack-reused 0 remote: . Processing 1 references remote: Processed 1 references in total To http://git.traefik.me:8888/alex/app.git 469db78..7fca3bf main -> main ``` Спустя некоторое время argocd получит информацию об изменениях в репозитории и создаст новую ревизию с версией `v2`: ![](img/argo-gitops10.png) А по адресу [app.traefik.me:8888](http://app.traefik.me:8888/) начнет отвечать новая версия: ![](img/argo-gitops11.png) Параметры чарта можно определять не только через git репозиторий, но и переназначать во вкладке `Parameters` в `Details` приложения argocd: ![](img/argo-gitops12.png) Может отредактировать, нажав кнопку `Edit` и вернуть параметр `image.tag` в значение `v1`: ![](img/argo-gitops13.png) Таким образом развернется приложение со старой версией: ![](img/argo-gitops14.png) [argocd]:https://argo-cd.readthedocs.io/en/stable/ [helm]:https://helm.sh/ [kubernetes]:https://kubernetes.io/ru/ [docker]:https://docs.docker.com/engine/ [kubectl]:https://kubernetes.io/ru/docs/reference/kubectl/kubectl/ [kind]:https://kind.sigs.k8s.io/ [kind-registry]:https://kind.sigs.k8s.io/docs/user/local-registry/ [gitea]:https://docs.gitea.com/