Gitlab - szablony
Witam
Praca jako konsultant jest cieżka. Masz tak dużo zadań do zrobenia. Dzień jest odrobine za krótki, ale możliwości do nauki są fantastyczne. Dziś mam troszkę czasu, aby zebrać moje notatki o GitLab, głównie części CI. Z drugiej strony jako repozutorium kodu dla organizacji, GitLab jest moim ulubionym rozwiązaniem. Szybki, łatwy w użyciu, macierz dostępów, oraz zarządzanie kluczami wdrażającymi. Wszystkie te rzeczy są super. Ogólnie to nie jest marketingowy wpis, ale mógłby :)
GitLabCI
GitLabCi jest bardzo popularny ze względu na łatwość użycia oraz szybkość configuracji, dodatkowo jest
za darmo - oczywiście jeżeli nie zostanie przekroczony darmowy limi (400 minut CI/CD miesięcnzie). Najważniejszym elementem jest tutaj konfiguracja początkowa. Ogranicza się ona do jednego pliku .gitlab-ci.yml
, umieszczonego w głównym folderze repozytorium.
Problem
W tym artykule nie ma problemów. To będzie tekst o szablonach, oraz o tym czemu są potrzebne. Załózmy, że mamy prosty przepływ wdrażający aplikacjię na Maszynę Wirtualną. Zwróć uwage na komentarze w kodzie.
image: ubuntu:latest
before_script:
- apt update
- apt install openssh-client -y
- eval $(ssh-agent -s)
# base64 is very usefull in case of id_rsa formating
- ssh-add <(echo "$DEPLOYER_PRIVATE_KEY" | base64 -d)
- mkdir -p ~/.ssh
- 'echo -e "Host *\n\tStrictHostKeyChecking no\n\n" > ~/.ssh/config'
stages:
# order of stages is important and is configure here
- create_env
- build
- deploy
# now we need to build .env file for compilation time
# that's tag driven deployment, so we deploy to particular
# env based on tag
build_the_dotfile_dev:
stage: create_env
script:
- echo "$DOTFILE_DEV" > .env
artifacts:
paths:
- .env
rules:
- if: '$CI_COMMIT_TAG =~ /^development/'
build_the_dotfile_stg:
stage: create_env
script:
- echo "$DOTFILE_STG" > .env
artifacts:
paths:
- .env
rules:
- if: '$CI_COMMIT_TAG =~ /^staging/'
build_the_dotfile_prod:
stage: create_env
script:
- echo "$DOTFILE_PROD" > .env
artifacts:
paths:
- .env
rules:
- if: '$CI_COMMIT_TAG =~ /^production/'
# now we build our app
# this step is generic
build_the_app:
image: node:12-buster
stage: build
script:
- git checkout -b '$CI_COMMIT_TAG'
- yarn install
- NODE_ENV=production yarn build
- tar --warning=no-file-changed --exclude=current.tar.gz --exclude-vcs -zcf current.tar.gz ./ || [[ $? -eq 1 ]]
artifacts:
paths:
- current.tar.gz
expire_in: 2 hrs
rules:
- if: '$CI_COMMIT_TAG =~ /^development|^staging|^production/'
# logic is simple
# 1. we transfer our artifact to VM
# 2. run the deployment script with some mv/pm2 logic
# 3. environment based on TAG again
deploy_front_to_dev:
stage: deploy
script:
- scp current.tar.gz deployer@$DEV_SERVER_IP:/home/appuser/front
- ssh deployer@$DEV_SERVER_IP "bash /home/appuser/deploy.sh front $CI_COMMIT_TAG"
rules:
- if: '$CI_COMMIT_TAG =~ /^development/'
deploy_afront_to_stg:
stage: deploy
script:
- scp current.tar.gz deployer@$STG_SERVER_IP:/home/appuser/front
- ssh deployer@$STG_SERVER_IP "bash /home/appuser/deploy.sh front $CI_COMMIT_TAG"
rules:
- if: '$CI_COMMIT_TAG =~ /^staging/'
deploy_front_to_prod:
stage: deploy
script:
- scp current.tar.gz deployer@$PROD_SERVER_IP:/home/appuser/front
- ssh deployer@$PROD_SERVER_IP "bash /home/appuser/deploy.sh front $CI_COMMIT_TAG"
rules:
- if: '$CI_COMMIT_TAG =~ /^production/'
Całkiem miłe, niestety dość długie rozwiązanie - 83 linie kodu. Dodatkowo dotarcza zbyt wielu możliwości popełnienia drobnych błędów w pisowni.
Rozwiązanie
Tutaj wczodzą szablony. To coś w rodzaju obiektu, który możemy wywołać z naszego przepływu. Po dodaniu szablonów nasz kod wygląda nastepująco:
stages:
- create_env
- build
- deploy
# note dot on the begining
# templates must start with dot, otherwise
# will be executed
.env_template: &envs
stage: create_env
image: alpine
script:
- echo "$DOTFILE" > .env
artifacts:
paths:
- .env
.deploy_template: &deploy_tmpl
stage: deploy
image: ubuntu:latest
before_script:
- apt update
- apt install openssh-client -y
- eval $(ssh-agent -s)
- ssh-add <(echo "$DEPLOYER_PRIVATE_KEY" | base64 -d)
- mkdir -p ~/.ssh
- 'echo -e "Host *\n\tStrictHostKeyChecking no\n\n" > ~/.ssh/config'
script:
- scp current.tar.gz deployer@$SERVER_IP:/home/appuser/front
- ssh deployer@$SERVER_IP "bash /home/appuser/bin/deploy.sh front $CI_COMMIT_TAG"
build_the_dotfile_dev:
variables:
DOTFILE: "$DOTFILE_DEV"
<<: *envs
rules:
- if: '$CI_COMMIT_TAG =~ /^development/'
build_the_dotfile_stg:
variables:
DOTFILE: "$DOTFILE_STG"
<<: *envs
rules:
- if: '$CI_COMMIT_TAG =~ /^staging/'
build_the_dotfile_prod:
variables:
DOTFILE: "$DOTFILE_PROD"
<<: *envs
rules:
- if: '$CI_COMMIT_TAG =~ /^production/'
build_the_app:
image: node:12.22.1-buster
stage: build
before_script:
- yarn policies set-version 1.22.10
script:
- git checkout -b '$CI_COMMIT_TAG'
- yarn install
- NODE_ENV=production yarn build
- tar --warning=no-file-changed --exclude=current.tar.gz --exclude-vcs -zcf current.tar.gz ./ || [[ $? -eq 1 ]]
artifacts:
paths:
- current.tar.gz
rules:
- if: '$CI_COMMIT_TAG =~ /^development|^staging|^production/'
deploy_front_to_dev:
variables:
SERVER_IP: "$DEV_SERVER_IP"
<<: *deploy_tmpl
rules:
- if: '$CI_COMMIT_TAG =~ /^development/'
deploy_afront_to_stg:
variables:
SERVER_IP: "$STG_SERVER_IP"
<<: *deploy_tmpl
rules:
- if: '$CI_COMMIT_TAG =~ /^staging/'
deploy_front_to_prod:
variables:
SERVER_IP: "$PROD_SERVER_IP"
<<: *deploy_tmpl
rules:
- if: '$CI_COMMIT_TAG =~ /^production/'
Zdaję sobie sprawę z tego, że kod jest wciąż długi, ale logika jest juz w 1 miejscu, a nie 3. Dodatkowo zarządzanie środowiskami będzie w przyszłości łatwe i czystę. Możliwe jest również wykorzystanie szablonów jako oddzielnych importowalnych plików.
Testowanie
Kolejny ciekawi temat, ważne jest testowanie składni przed umieszczeniem kodu na środowisku deweloperskim. W tym celu wystarczy użyć łatwej metody to testowania, Twoich scen. Jedyną rzeczą jaką potrzebujesz jest uruchomienie poniższej komendy w Twoim localnym repozytorium.
docker run -d \
--name gitlab-runner \
--restart always \
-v $PWD:$PWD \
-v /var/run/docker.sock:/var/run/docker.sock \
gitlab/gitlab-runner:latest
Następnie uruchom wybrane zadanie.
docker exec -it -w $PWD gitlab-runner gitlab-runner exec docker <task name>
Jest tutaj jeden problem który sprawia, że chce mi się płakać… Nie mogę znaleść opcji, aby urchomić cały przepływ. Uruchamianie scen jest w porządku, niesety nie ma opcji, aby przetestować pamięć podreczną, lub wymiane plików pomiędzy zadaniami.
Podsumowanie
Podsumowanie jest zbyteczne. Szabony są czytelne i łatwe w użyciu. GitLab jest świetny, niesety życie jest trudne. Mam 27 lat, wczoraj kupiłem sobie pierwszy kask rowerowy. Bezpieczeństwo przedewszystkim. Trzymaj się.