{"id":4540,"date":"2022-05-27T13:58:00","date_gmt":"2022-05-27T11:58:00","guid":{"rendered":"https:\/\/blog.besharp.it\/?p=4540"},"modified":"2022-06-30T15:32:21","modified_gmt":"2022-06-30T13:32:21","slug":"un-approccio-serverless-per-lintegrazione-con-gitlab-su-aws","status":"publish","type":"post","link":"https:\/\/blog.besharp.it\/it\/un-approccio-serverless-per-lintegrazione-con-gitlab-su-aws\/","title":{"rendered":"Un approccio serverless per l’integrazione con GitLab su AWS"},"content":{"rendered":"\n
L\u2019ottimizzazione dei costi e l\u2019eccellenza operativa sono fattori determinanti per una strategia vincente, che porti all\u2019adozione del paradigma Cloud. I servizi gestiti serverless consentono di ridurre drasticamente i costi e di velocizzare le operazioni di mantenimento dell\u2019infrastruttura.<\/p>\n\n\n\n
In questo articolo descriveremo come integrare le pipeline GitLab su AWS usando ECS Fargate in uno scenario multi-ambiente.<\/p>\n\n\n\n
GitLab, per quanto riguarda l\u2019utilizzo di risorse computazionali, offre una grande flessibilit\u00e0: le pipeline possono essere eseguite da cluster Kuberenetes, Docker, su virtual machine on prem o su piattaforme personalizzate usando driver custom.<\/p>\n\n\n\n
La soluzione pi\u00f9 diffusa per l\u2019esecuzione di pipeline sul Cloud AWS prevede l\u2019utilizzo di istanze EC2. Questo approccio porta ad alcune inefficienze: avviare le istanze on-demand aumenta il tempo di esecuzione, rendendo impazienti gli sviluppatori (a causa del tempo di inizializzazione). Tenere attive istanze \u201cdi scorta\u201d durante la giornata disponibili per le build, di contro, aumenta i costi. <\/p>\n\n\n\n
Il nostro obiettivo \u00e8 trovare una soluzione che possa ridurre il tempo di esecuzione, facilitare la manutenzione e ottimizzare i costi.<\/p>\n\n\n\n
I container, ad esempio, hanno un tempo di avvio breve e aiutano a contenere i costi: in questo caso d\u2019uso il billing sarebbe proporzionale solamente al tempo di esecuzione effettivamente utilizzato. <\/p>\n\n\n\n
Per raggiungere il nostro obiettivo faremo in modo di eseguire su task ECS Fargate le nostre build. In aggiunta vedremo anche come utilizzare i servizi ECS per implementarle e gestirne l\u2019autoscaling. <\/p>\n\n\n\n
Prima di passare all\u2019implementazione pratica una piccola premessa: GitLab, per eseguire gli script definiti nelle pipeline, utilizza un agent software chiamato GitLab Runner, possiamo configurare una istanza del runner dedicata alla gestione dello scaling che si occupi di aggiungere e rimuovere risorse computazionali in base all\u2019andamento delle richieste.<\/p>\n\n\n\n
Nei nostri esempi assumeremo che siano utilizzati tre ambienti: sviluppo (dev), test (staging) e produzione (prod), utilizzeremo ruoli IAM differenti per i nostri runner per limitare i permessi assegnati ed utilizzare il principio di least privilege.<\/p>\n\n\n\n
I runner GitLab possono essere associati a tag che rendono possibile la scelta dell\u2019ambiente in cui verranno eseguite le build.<\/p>\n\n\n\n
In questo esempio \u00e8 definita una pipeline che compila e fa il deploy in 3 ambienti differenti:<\/p>\n\n\n\n
stages: \n - build dev\n - deploy dev \n - build staging\n - deploy staging\n - build production\n - deploy production\n \nbuild-dev: \n stage: build dev \n tags: \n - dev \n script: \n - .\/scripts\/build.sh\n artifacts: \n paths: \n - .\/artifacts\n expire_in: 7d \n \ndeploy-dev: \n stage: deploy dev \n tags: \n - dev \n script: \n - .\/scripts\/deploy.sh\n\nbuild-staging: \n stage: build staging\n tags: \n - staging\n script: \n - .\/scripts\/build.sh\n artifacts: \n paths: \n - .\/artifacts\n expire_in: 7d \n\n deploy-staging: \n stage: deploy staging\n tags: \n - staging\n script: \n - .\/scripts\/deploy.sh\n\nbuild-production: \n stage: build production\n tags: \n - production\n script: \n - .\/scripts\/build.sh\n artifacts: \n paths: \n - .\/artifacts\n expire_in: 7d \n\n deploy-production: \n stage: deploy production\n tags: \n - production\n script: \n - .\/scripts\/deploy.sh\n<\/code><\/pre>\n\n\n\nImplementare un runner Fargate base<\/strong><\/h2>\n\n\n\nAssumendo che il nostro software sia scritto utilizzando NodeJS possiamo sviluppare un Dockerfile che permetta di fare la build di una immagine Docker contenente tutte le dipendenze (GitLab runner incluso).<\/p>\n\n\n\n
Dockerfile<\/strong><\/p>\n\n\n\n# Ubuntu based GitLab runner with nodeJS, npm, and aws CLI \n# --------------------------------------------------------------------- \n# Install https:\/\/github.com\/krallin\/tini - a very small 'init' process \n# that helps process signals sent to the container properly. \n# --------------------------------------------------------------------- \nARG TINI_VERSION=v0.19.0 \n \nCOPY docker-entrypoint.sh \/usr\/local\/bin\/docker-entrypoint.sh \n \nRUN ln -snf \/usr\/share\/zoneinfo\/Europe\/Rome \/etc\/localtime && echo Europe\/Rome > \/etc\/timezone \\ \n && echo \"Installing base packaes\" \\ \n && apt update && apt install -y curl gnupg unzip jq software-properties-common \\ \n && echo \"Installing awscli\" \\ \n && curl \"https:\/\/awscli.amazonaws.com\/awscli-exe-linux-x86_64.zip\" -o \"awscliv2.zip\" \\ \n && unzip awscliv2.zip \\ \n && .\/aws\/install \\ \n && rm -f awscliv2.zip \\ \n && apt update \\ \n && echo \"Installing packages\" \\ \n && apt install -y unzip openssh-server ca-certificates git git-lfs nodejs npm \\ \n && echo \"Installing tini and ssh\" \\ \n && curl -Lo \/usr\/local\/bin\/tini https:\/\/github.com\/krallin\/tini\/releases\/download\/${TINI_VERSION}\/tini-amd64 \\ \n && chmod +x \/usr\/local\/bin\/tini \\ \n && mkdir -p \/run\/sshd \\ \n && curl -L https:\/\/packages.gitlab.com\/install\/repositories\/runner\/gitlab-runner\/script.deb.sh | bash \\ \n && apt install -y gitlab-runner \\ \n && rm -rf \/var\/lib\/apt\/lists\/* \\ \n && rm -f \/home\/gitlab-runner\/.bash_logout \\ \n && git lfs install --skip-repo \\ \n && chmod +x \/usr\/local\/bin\/docker-entrypoint.sh \\ \n && echo \"Done\"\n\nEXPOSE 22 \n \nENTRYPOINT [\"tini\", \"--\", \"\/usr\/local\/bin\/docker-entrypoint.sh\"]<\/code><\/pre>\n\n\n\ndocker-entrypoint.sh<\/strong><\/p>\n\n\n\n#!\/bin\/sh \n \n# Create a folder to store the user's SSH keys if it does not exist. \nUSER_SSH_KEYS_FOLDER=~\/.ssh \n[ ! -d ${USER_SSH_KEYS_FOLDER} ] && mkdir -p ${USER_SSH_KEYS_FOLDER} \n \n# Copy contents from the `SSH_PUBLIC_KEY` environment variable \n# to the `$USER_SSH_KEYS_FOLDER\/authorized_keys` file. \n# The environment variable must be set when the container starts. \necho \"${SSH_PUBLIC_KEY}\" > ${USER_SSH_KEYS_FOLDER}\/authorized_keys \n \n# Clear the `SSH_PUBLIC_KEY` environment variable. \nunset SSH_PUBLIC_KEY \n \n# Start the SSH daemon \n\/usr\/sbin\/sshd -D<\/code><\/pre>\n\n\n\n<\/p>\n\n\n\n
<\/p>\n\n\n\n
In questo caso non c\u2019\u00e8 nessuna dipendenza dall\u2019ambiente in cui l\u2019immagine dovr\u00e0 essere eseguita.<\/p>\n\n\n\n
Implementare un runner per l\u2019autoscaling (Runner Manager)<\/strong><\/p>\n\n\n\nQuesto tipo di runner deve essere specializzato per poter gestire l\u2019ambiente in cui dovr\u00e0 essere eseguito: adotteremo il Fargate custom executor sviluppato da GitLab per utilizzare cluster ECS Fargate differenti per i nostri ambienti.<\/p>\n\n\n\n
La registrazione del nostro runner con il server GitLab sar\u00e0 automatizzata ed eseguita durante la fase di build, usando variabili per specificare il token e gli altri parametri di configurazione.<\/p>\n\n\n\n
Il custom executor Fargate necessita di un file di configurazione (\u201cconfig.toml\u201d) per poter specificare il cluster, le subnet, i security groups e la task definition da utilizzare per l\u2019esecuzione della pipeline. Faremo in modo di automatizzare anche questa configurazione nella fase di build del container.<\/p>\n\n\n\n
Per prima cosa dobbiamo ottenere un token per la registrazione del runner con il server GitLab.<\/p>\n\n\n\n
Alla sezione \u201cCI\/CD\u201d nelle impostazioni del progetto si pu\u00f2 espandere la sezione \u201cRunners\u201d.<\/p>\n\n\n\n
<\/p>\n\n\n\n