Skip to main content

Heberger un site statique sur S3, CI/CD avec gitlab-ci

· 8 min read
Fabien Villemaine

Présentation

Vous souhaitez héberger un site statique mais n’avez pas envie de devoir gérer l’installation d’un serveur web et la configuration d’un apache/nginx/…, il existe aujourd’hui des alternatives, sans aucune configuration de machine, sans maintenance, supportant les pics de charge et très performant.

Je vous présente ici une solution reposant sur les services fournis par AWS, ainsi qu’une gestion du site au travers de Gitlab ou Azure Devops et d’un pipeline de CI/CD.

Nous avons besoin de :

  • un bucket S3 (non public) pour héberger les données du site

  • un CDN, Cloudfront

  • Un runner (EC2, runner Azure, ...)

Si votre nom de domaine est géré par un tiers (OVH, Ghandi, …), il faudra faire quelques manipulations supplémentaires décrites plus bas, ici nous verrons pour la délégation d’un sous-domaine.

La création du certificat et son renouvellement sera là aussi complètement géré par AWS.

Création du site docusaurus

Nous avons fait le choix de docusaurus pour sa simplicité, sa légèreté, et sa communauté active qui montre l’engouement envers ce projet open-source.

Pour en savoir plus : https://docusaurus.io

Installation des dépendances

La première chose est de préparer le site, qui a besoin de certaines dépendances pour pouvoir être généré.

Voici les dépendances requises à la date de rédaction de l'article (2.0.0-beta.9) :

  • Node.js version >= 14 ou supérieure
  • Yarn version >= 1.5

Nous installons donc en local les dépendances (si vous êtes sous Linux ou via WSL sur un poste Windows par exemple)

apt install yarn
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.0/install.sh | bash

On peut vérifier l’installation de nvm (après avoir relancé le terminal)

command -v nvm

Installation de nodejs en version 14.18.1 (quelques soucis avec les dernières versions)

nvm install 14.18.1

Génération de l’arborescence Docusaurus

Pour générer l’arborescence du site

npm init docusaurus@latest nom_du_projet classic

classic représente le template sélectionné.

Et voilà, la base du site est prête !

Vous pouvez configurer le site à votre convenance (je vous renvoie une nouvelle fois vers la doc ; très bien faire ; de docusaurus -> https://docusaurus.io/fr/docs/configuration)

Pour tester le site rapidement (vous voyez même les changements en direct dès qu'un fichier est modifié)

npm start

Génération du build

Depuis le dossier de votre projet, lancer le build

npm run build

Cela va générer un dossier build contenant tous les fichiers nécessaires.

Vous pouvez tester ce build avec cette commande :

npm run serve

Création de l’infra AWS

Maintenant que vous avez le contenu du site, on va pouvoir créer l’infra qui va l’héberger chez AWS.

Nom de domaine

Comme précisé plus tôt, si votre gestionnaire de nom de domaine est externe à AWS, pas de panique, il n’y a pas grand-chose à faire de plus pour déléguer le sous-domaine.

Dans votre console AWS, vous devez créer une nouvelle Hosted zones correspondant au nom de domaine.

Puis côté OVH (par exemple) vous devez ajouter les 4 entrés NS générées par AWS en spécifiant le sous-domaine voulu.

Terraform

Nous utilisons Terraform pour des questions pratiques. Il s’agit d’un outil logiciel permettant d’automatiser la création et la gestion des infrastructures IT dans le cloud. Si vous ne connaissez pas cet outils d’Infra-As-Code, je vous conseille très fortement de vous y intéresser ! Cela fera d'ailleurs l'objet d'un futur article.

Pour télécharger le template =>

Pensez à modifier les valeurs des variables dans le fichier main.auto.tfvars ainsi que le fichier providers.tf

Vous aurez besoin de spécifier une Key pour la création de la machine, si vous n’en avez pas encore, vous pouvez en générer une sur la console AWS, au travers de EC2 / (Key Pairs)

L’EC2 créée sert de runner gitlab-ci, et est automatiquement ajoutée en tant que runner actif à votre projet via les commandes passées au démarrage de la machine (grâce au bloc user_data).

Pensez à bien récupérer le registration-token sur votre projet Gitlab, dans settings/ci_cd#js-runners-settings

Si vous souhaitez le faire à la main :

sudo apt update
sudo curl -L --output /usr/local/bin/gitlab-runner https://gitlab-runner-downloads.s3.amazonaws.com/latest/binaries/gitlab-runner-linux-amd64
sudo chmod +x /usr/local/bin/gitlab-runner
sudo useradd --comment 'GitLab Runner' --create-home gitlab-runner --shell /bin/bash
sudo gitlab-runner install --user=gitlab-runner --working-directory=/home/gitlab-runner
sudo gitlab-runner start
sudo gitlab-runner register --non-interactive --url https://gitlab.com/ --registration-token 0123456798azerty --executor docker+machine --docker-image registry.gitlab.com/gitlab-org/cloud-deploy/aws-base:latest --description "runner" --tag-list "docker,aws" --run-untagged="true" --locked="false" --access-level="not_protected"

Si vous n’avez pas Terraform d’installé

curl -fsSL https://apt.releases.hashicorp.com/gpg | sudo apt-key add –
sudo apt-add-repository "deb [arch=$(dpkg --print-architecture)] https://apt.releases.hashicorp.com $(lsb_release -cs) main"
sudo apt install terraform

Vous avez aussi besoin d’AWS CLI

apt install awscli

Puis de configurer un profil AWS ayant les droits nécessaires au déploiement

aws configure

Si vous avez bien tout configuré, vous pouvez passer au déploiement de l’infra

Depuis le dossier Terraform

terraform init
terraform apply

Après quelques minutes, l’infra devrait être complètement déployée et fonctionnelle.

Git

Si ce n’est déjà fait, vous pouvez déposer le dossier build dans votre dépôt git.

Désactivez les Shared runners dans les paramètres d'Intégration et livraison continue pour éviter d'être bloqué en mode docker+machine (ce qui empechera l'utilisation du role IAM)

Puis à la racine, créez un fichier .gitlab-ci.yml

image: registry.gitlab.com/gitlab-org/cloud-deploy/aws-base:latest
variables:
GIT_SUBMODULE_STRATEGY: recursive
AWS_DEFAULT_REGION: eu-west-3
BUCKET_NAME: le_nom_de_votre_bucket
CLOUDFRONT_ID : id_du_cloudfont
deploy:
stage: deploy
script:
- aws --region $AWS_DEFAULT_REGION s3 sync ./build s3://$BUCKET_NAME/site --delete
- aws --region $AWS_DEFAULT_REGION cloudfront create-invalidation --distribution-id $CLOUDFRONT_ID --paths "/*"
when: manual
only:
- main

Vous pouvez lancer le pipeline créé via le menu Intégration et livraison continue

Si tout se passe bien, le contenu du dossier build devrait être poussé sur le bucket S3, puis une invalidation du Cloudfront devrait se lancer pour rafraichir le cache, et ainsi voir le site publié.

Azure-Devops

Si vous ne voulez pas utiliser gitlab, vous pouvez utiliser d’autres gestionnaires de CI/CD, comme Azure-DevOps par exemple.

Vous pouvez réutiliser le runner créé précédemment (en installant l'agent sur la machine), ou bien en passant par les runner Microsoft.

Vous avez accès gratuitement à des runners grace à l'offre Free tier (1 parallel job up to 1800 mins/mo), mais vous êtes obligé de créer des credentials IAM.

Vous aurez besoin d'installer l'extension AWS Toolkit for Azure DevOps depuis la marketplace.

Pour renseigner les Access Key et Secret Key AWS (pensez bien aux droits nécessaires, vous pouvez reprendre la policy décrite dans le terraform), ajouter les valeurs dans la section Service connections des paramètres de votre projet Azure DevOps.

Créez un nouveau pipeline qui servira à builder le site puis pousser les données sur le bucket S3 et enfin créer l'invalidation Cloudfront.

trigger:
- main
stages:
- stage: Build
jobs:
- job: Build
pool:
vmImage: "ubuntu-latest"
steps:
- task: NodeTool@0
inputs:
versionSpec: '14.x'
checkLatest: true
displayName: "Install Node.js"
- script: |
yarn
yarn build
displayName: "yarn and yarn build"
workingDirectory: "docusaurus/"
- task: CopyFiles@2
inputs:
SourceFolder: "docusaurus/build"
Contents: "**"
TargetFolder: "$(Build.ArtifactStagingDirectory)"
- task: PublishBuildArtifacts@1
inputs:
PathtoPublish: '$(Build.ArtifactStagingDirectory)'
ArtifactName: 'site'
publishLocation: 'Container'
- stage: Deployment
displayName: Deploy to S3
dependsOn: Build
condition: succeeded()
jobs:
- deployment:
pool:
vmImage: "ubuntu-latest"
environment: prod
strategy:
runOnce:
deploy:
steps:
- task: S3Upload@1
inputs:
awsCredentials: 'YOUR_AWS_SERVICE_CONNECTION'
regionName: 'YOUR_AWS_REGION_NAME'
bucketName: 'YOUR_BUCKET_NAME'
sourceFolder: '$(Pipeline.Workspace)'
globExpressions: '**'
keyManagement: 'awsManaged'
encryptionAlgorithm: 'AES256'
- task: AWSCLI@1
inputs:
awsCredentials: "YOUR_AWS_SERVICE_CONNECTION"
regionName: 'YOUR_AWS_REGION_NAME'
awsCommand: "cloudfront"
awsSubCommand: "create-invalidation"
awsArguments: '--distribution-id YOUR_DISTRIBUTION_ID --paths "/*"'
displayName: "Invalidate CloudFront Cache"
attention

Pensez à renseigner le fichier avec vos valeurs !

Mises à jour du contenu

Pour éditer ou ajouter du contenu, vous avez juste à mettre à jour les fichiers du dossier de votre projet docusaurus, puis relancer un build

npm run build

Enfin vous pouvez relancer votre pipeline gitlab afin de déployer les changements en live

Mises à jour docusaurus

Pour mettre à jour la version de docusaurus, vous pouvez suivre la documentation officielle

Mettre à jour la version dans le fichier package.json

"dependencies": {
"@docusaurus/core": "^2.0.0-beta.9",
"@docusaurus/preset-classic": "^2.0.0-beta.9",
// ...
}

Puis lancez la commande

npm install

Puis pour vérifier que la mise à jour s'est bien été effectuée

npx docusaurus --version

Conclusion

Comme vous avez pu le voir, la mise en place d'un site statique sur une infra managée est assez simple.

La gestion du site est réduite au minimum, il faut juste penser de temps en temps à mettre à jour l'outil pour profiter des corrections des failles de sécurité ainsi que des améliorations apportées au fil des mises à jour de docusaurus.