Skip to main content

Simplifier la création d’images de conteneur avec Ko

Écrit par
feature building go images

10 octobre 2022

0 minutes de lecture

Dans un article précédent, j’ai expliqué comment — et pourquoi — vous pourriez utiliser l’outil Jib du groupe Google Open Source pour créer des images de conteneur pour vos applications Java. Jib crée des images légères, basées sur la JVM et conformes à OCI, qui respectent les bonnes pratiques sans nécessiter de moteur d’exécution de conteneur comme Docker, et vous évite d’écrire et de gérer des Dockerfiles. Mais si vous développez des applications en Go ? Il existe un autre outil open source pour Go, qui fonctionne de façon similaire : Ko.

Remarque : Au cours de la rédaction de cet article, le projet Ko a été transféré d’un dépôt GitHub appartenant à Google vers sa propre organisation de premier niveau, « ko-build ». Nous avons mis à jour le texte de cet article en conséquence, mais vous pourrez encore trouver en ligne des références à « Google Ko » et ce nom pourra figurer dans des noms de modules pendant quelque temps. Rassurez-vous, il s’agit bien du même projet. Félicitations à l’équipe de Ko pour cette réussite qui a motivé le transfert !

Dans cet article, nous verrons comment utiliser Ko pour créer des images de conteneur sans Dockerfile, générer des SBOM et intégrer Kubernetes.

Qu’est-ce que Ko ?

Ko est un outil en ligne de commande fourni sous la forme d’un binaire unique, conçu pour remplacer l’exécution du compilateur go dans votre processus de développement. En plus de compiler votre application, il crée une image de conteneur ultralégère qui contient votre application. Comme Jib, Ko envoie l’image vers un registre ou la place dans le cache local d’images Docker, selon sa configuration et son mode d’exécution.

Ko propose également quelques fonctionnalités supplémentaires pour la création de nomenclatures logicielles (SBOM) et l’intégration à Kubernetes, afin de simplifier considérablement les cycles de développement et de déploiement itératifs.

Quels problèmes Ko cherche-t-il à résoudre ?

De nombreux développeurs, quel que soit le langage avec lequel ils travaillent, découvrent la création de conteneurs et se posent souvent beaucoup de questions sur la création d’images, par exemple :

  • Quelle image de base choisir, et est-elle conforme aux politiques de mon organisation ?

  • Comment combiner au mieux les commandes pour limiter le gonflement des couches ?

  • Quelle partie de mon application dois-je copier dans l’image pour pouvoir l’exécuter ?

  • Quels outils dois-je apprendre à utiliser pour créer l’image ? (Docker ? Buildah ? BuildKit ?)

  • Existe-t-il des annotations standard que je dois inclure conformément aux exigences de mon organisation ?

Les architectes et responsables d’applications souhaitent également faciliter l’adoption de la gouvernance et des normes par leurs équipes. Cependant, maintenir des pratiques uniformes peut s’avérer difficile lorsque chaque équipe a élaboré ses propres modèles de Dockerfile.

Les équipes de sécurité s’intéressent aussi de près au contenu des images et aux outils utilisés pour les créer. Par exemple, exposer le moteur Docker — ou le docker.sock de la machine hôte — à un nœud de compilation CI peut accorder des privilèges élevés aux environnements de compilation sur ces nœuds.

« Le démon [Docker]… dispose de nombreuses fonctionnalités qui vont bien au-delà de la création d’images et de l’interaction avec les registres. Sans outils de sécurité supplémentaires, tout utilisateur capable de lancer une commande docker build sur cette machine peut également exécuter une commande docker run pour lancer n’importe quelle commande sur la machine… Non seulement il peut lancer les commandes de son choix, mais s’il utilise ces privilèges pour mener une action malveillante, il sera difficile de déterminer qui en est responsable. »

— Container Security de Liz Rice, chapitre 6

À l’inverse, l’introduction de nouveaux outils de compilation peut être complexe et allonger la courbe d’apprentissage des personnes responsables des systèmes de compilation. Ko vise à répondre à chacun de ces enjeux, tout en offrant une expérience optimale aux développeurs qui l’utilisent.

Créer des images avec et sans Ko

Pour comprendre comment Ko relève les défis ci-dessus, commençons par comparer son fonctionnement aux étapes habituelles de compilation Go et Docker.

Créer une image sans Ko

Pour cet exemple, nous allons créer l’application classique du tutoriel Go : l’application Web « Hello World ».

1. Dans un répertoire vide, créez le fichier suivant sous le nom hello.go :

package main

import (
"fmt"
"net/http"
)

func main() {
http.HandleFunc("/", HelloWebServer)
http.ListenAndServe(":8080", nil)
}

func HelloWebServer(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello, %s!", r.URL.Path[1:])
}

2. Nous allons compiler le binaire dans notre Dockerfile, une pratique courante :

FROM golang AS build
WORKDIR /go/src
COPY . .
RUN GOOS=linux go build -ldflags "-linkmode external -extldflags -static" -a hello.go

FROM gcr.io/distroless/static:nonroot
USER nonroot:nonroot
COPY --from=build /go/src/hello .
EXPOSE 8080
CMD ["./hello"]

Il s’agit d’une compilation en plusieurs étapes. La première étape, nommée build, est basée sur l’image officielle golang. Nous y copions le contenu de notre application (pour l’instant, il s’agit simplement du fichier hello.go) dans l’image, sous /go/src, puis exécutons l’outil go build avec les paramètres nécessaires pour produire un binaire statique.

Pour la deuxième étape, nous partons de l’image de base static:nonroot du projet Google Distroless, définissons nonroot comme utilisateur par défaut, copions le binaire hello depuis l’étape build dans le répertoire racine, définissons des métadonnées sur le port à exposer et indiquons que ./hello doit être exécuté par défaut au démarrage du conteneur.

Pourquoi deux étapes ?

Il est courant de compiler votre application dans le Dockerfile, car cela garantit que la même version du compilateur est utilisée sur le poste de développement et dans les compilations automatisées. Une erreur fréquente consiste toutefois à déployer l’image qui a servi à la compilation. Cela représente un risque de sécurité, car l’image déployée contient alors non seulement des outils superflus comme le compilateur, mais aussi le code source de votre application. Si un attaquant parvient à exploiter une vulnérabilité ou à obtenir une copie de votre image, il disposera de nombreuses informations et de nombreux outils pour étendre son attaque. En partant de l’image de base distroless/static:nonroot, nous obtenons un système de fichiers extrêmement minimal, avec un utilisateur non root par défaut.

3. Une fois notre Dockerfile prêt, nous pouvons utiliser la commande docker build pour créer une image dans le cache local et lui attribuer une étiquette :

$ docker build -t localhost:5000/hello-go:1.2.3 .
[+] Building 5.6s (12/12) FINISHED
 => [internal] load build definition from Dockerfile
 => => transferring dockerfile: 298B
 => [internal] load .dockerignore
 => => transferring context: 73B
 => [internal] load metadata for gcr.io/distroless/static:nonroot
 => [internal] load metadata for docker.io/library/golang:latest
 => [stage-1 1/2] FROM gcr.io/distroless/static:nonroot
 => CACHED [build 1/4] FROM docker.io/library/golang
 => [internal] load build context
 => => transferring context: 5.15kB
 => [build 2/4] WORKDIR /go/src
 => [build 3/4] COPY . .
 => [build 4/4] RUN GOOS=linux go build -ldflags "-linkmode external -extldflags -static" -a hello.go
 => [stage-1 2/2] COPY --from=build /go/src/hello .
 => exporting to image
 => => exporting layers
 => => writing image sha256:eb9735e61e1dec63bd557dd5c61d8789733f2f4456a0d01687816bd0d135a7bc
 => => naming to localhost:5000/hello-go:1.2.3

$ docker images
REPOSITORY               TAG    IMAGE ID      CREATED         SIZE
localhost:5000/hello-go  1.2.3  eb9735e61e1d  11 minutes ago  9.23MB

Remarque : Le résultat peut varier selon la version de Docker que vous utilisez et selon que les améliorations BuildKit sont activées ou non.

4. Pour que d’autres personnes puissent utiliser cette image, nous devons l’envoyer vers un dépôt de registre. Dans cet exemple, j’exécute un dépôt local sur mon poste, au port 5000, à l’aide de l’image registry:2 de DockerHub. (C’est pour cette raison que j’ai attribué à l’image l’étiquette avec le préfixe localhost:5000/.)

Avec Docker, envoyer une image vers un registre est assez simple : il suffit d’utiliser la commande docker push :

$ docker push localhost:5000/hello-go:1.2.3
The push refers to repository [localhost:5000/hello-go]
0ba424468cb9: Pushed
ca623f32e759: Pushed
1.2.3: digest: sha256:ddcabff499d90fdf6850ef0b7addb33db7def…

Remarque : S’il s’agissait d’un registre géré, je devrais d’abord m’y authentifier avec docker login.

5. Enfin, pour exécuter notre image, nous pouvons utiliser docker run ou un déploiement kubectl approprié. Par souci de simplicité, nous allons choisir la première option :

$ docker run --rm -d -p 8080:8080 localhost:5000/hello-go:1.2.3
Unable to find image 'localhost:5000/hello-go:1.2.3' locally
1.2.3: Pulling from hello-go
45e68f4d0d8c: Already exists
ce1a03145a01: Already exists
Digest: sha256:ddcabff499d90fdf6850ef0b7addb33db7defe4669f9af1079894e84ad407199
Status: Downloaded newer image for localhost:5000/hello-go:1.2.3
2ef3c3dc0363ad10e9bf21baa7e78c63ca4df904bcba1fdab3af7732fce3857d

et nous pouvons tester l’application avec une simple commande curl :

$ curl http://localhost:8080/Patch
Hello, Patch!

Voici à quoi pourraient ressembler les étapes que nous venons de suivre, représentées sous forme de schéma :

Diagramme illustrant le workflow Go et Docker, de la compilation Go et du Dockerfile à l’image Docker, à son envoi vers un registre, puis à son exécution dans un conteneur ou sur Kubernetes.

Revenons maintenant au début pour voir comment procéder avec Ko.

Créer une image avec Ko

1. Commençons par le même fichier hello.go, dans un répertoire vide :

package main

import (
"fmt"
"net/http"
)

func main() {
http.HandleFunc("/", HelloWebServer)
http.ListenAndServe(":8080", nil)
}

func HelloWebServer(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello, %s!", r.URL.Path[1:])
}

2. Ensuite, définissons le registre d’images dans une variable d’environnement et exécutons ko build :

$ export KO_DOCKER_REPO=localhost:5000
$ ko build hello.go
2022/09/19 14:12:37 No matching credentials were found, falling back on anonymous
2022/09/19 14:12:40 Using base gcr.io/distroless/static:nonroot@sha256:2a9e2b4fa771d31fe3346a873be845bfc2159695b9f90ca08e950497006ccc2e for hello.go
2022/09/19 14:12:40 Building hello.go for linux/amd64
2022/09/19 14:12:44 Publishing localhost:5000/hello.go-7a204cfb24536a350234d9132276cae7:latest
2022/09/19 14:12:44 existing blob: sha256:2952e4f69ebf4bea5cc557f73626f95649cb546424fd998481ba690a08d9db7f
2022/09/19 14:12:44 existing blob: sha256:a706af0bb599ee120bd57c0e6abca55f66fd714f9e74706d9c97a583fc79d37e
2022/09/19 14:12:44 localhost:5000/hello.go-7a204cfb24536a350234d9132276cae7:sha256-3925979ac92afb8cb89fc80438097873daf067195e4d0b9d2fd6f55d6201355c.sbom: digest: sha256:7d444debc3cd2d5545e88606dc529fa7ed90b1bb581ff8ac30b0f475b68d4ec0 size: 367
2022/09/19 14:12:44 Published SBOM localhost:5000/hello.go-7a204cfb24536a350234d9132276cae7:sha256-3925979ac92afb8cb89fc80438097873daf067195e4d0b9d2fd6f55d6201355c.sbom
2022/09/19 14:12:44 existing blob: sha256:5ec5232d47ab0ad088792a191b672fe6ec27db63b19daebd7322ad64a2cd8676
2022/09/19 14:12:44 existing blob: sha256:250c06f7c38e52dc77e5c7586c3e40280dc7ff9bb9007c396e06d96736cf8542
2022/09/19 14:12:45 pushed blob: sha256:2d23903e55394a021ba4936cfad8ccbec6998413164416fcae4f6bf888665fce
2022/09/19 14:12:45 pushed blob: sha256:1cd0595314a53d179ddaf68761c9f40c4d9d1bcd3f692d1c005938dac2993db6
2022/09/19 14:12:45 localhost:5000/hello.go-7a204cfb24536a350234d9132276cae7:latest: digest: sha256:3925979ac92afb8cb89fc80438097873daf067195e4d0b9d2fd6f55d6201355c size: 750
2022/09/19 14:12:45 Published localhost:5000/hello.go-7a204cfb24536a350234d9132276cae7@sha256:3925979ac92afb8cb89fc80438097873daf067195e4d0b9d2fd6f55d6201355c
localhost:5000/hello.go-7a204cfb24536a350234d9132276cae7@sha256:3925979ac92afb8cb89fc80438097873daf067195e4d0b9d2fd6f55d6201355c

Cette commande :

  • a compilé notre application en un binaire statique ;

  • l’a intégrée dans une image bien configurée ;

  • et l’a envoyée vers mon registre.

Aucun Dockerfile ni moteur d’exécution de conteneur n’a été nécessaire. Vous remarquerez également dans le résultat des références à la création et à la publication d’une SBOM ; nous y reviendrons plus tard.

Remarque : L’étiquette de l’image contient ici un hachage MD5 qui correspond, par défaut, au chemin d’importation de votre application Go. Comme nous n’avons pas de module Go complet dans cet exemple, ce hachage est simplement calculé à partir de hello.go. Consultez la documentation de Ko pour en savoir plus sur les étiquettes.

3. Nous allons maintenant exécuter l’image avec docker run :

$ docker run --rm -d -p 8080:8080 localhost:5000/hello.go-7a204cfb24536a350234d9132276cae7:latest
Unable to find image 'localhost:5000/hello.go-7a204cfb24536a350234d9132276cae7:latest' locally
latest: Pulling from hello.go-7a204cfb24536a350234d9132276cae7
1cd0595314a5: Pull complete
250c06f7c38e: Pull complete
5ec5232d47ab: Pull complete
Digest: sha256:3925979ac92afb8cb89fc80438097873daf067195e4d0b9d2fd6f55d6201355c
Status: Downloaded newer image for localhost:5000/hello.go-7a204cfb24536a350234d9132276cae7:latest
WARNING: The requested image's platform (linux/amd64) does not match the detected host platform (linux/arm64/v8) and no specific platform was requested
70877dcd53d23c66873e8e1e5a2092b46740e699b214a545e386113e5e098d7c

... et la tester avec curl :

curl http://localhost:8080/ko
Hello, ko!

Voici à quoi pourrait ressembler le processus Ko sous forme de schéma :

Schéma montrant KO Build qui envoie des images vers un registre, puis Docker ou Kubernetes qui exécute un conteneur en récupérant implicitement une image

En confiant à Ko les principales étapes de création, de configuration et d’envoi des images, nous avons réduit le nombre d’étapes à effectuer, la dépendance au moteur d’exécution de conteneur au moment de la compilation, ainsi que le niveau d’expertise et les besoins de maintenance associés au Dockerfile.

Des valeurs par défaut judicieuses, des adaptations déterministes

Les utilisateurs de Ko bénéficient des bonnes pratiques définies collectivement par la communauté open source. Mais que faire si les normes propres à votre organisation ne sont pas compatibles avec celles-ci ? Les options de ligne de commande de Ko et/ou son fichier de configuration ko.yaml peuvent alors vous être utiles.

Image de base personnalisée

Supposons, par exemple, que votre entreprise exige une image spécifique comme base pour toutes les applications Go. Il vous suffit d’ajouter une ligne defaultBaseImage: dans un fichier ko.yaml placé dans le répertoire racine, et d’y indiquer cette image.

defaultBaseImage: repo.mycorp.com/myteam/corp-approved-scratch:220923

Options du compilateur Go

Pour spécifier explicitement les options -ldflags du compilateur Go, comme dans notre exemple initial, ajoutez simplement une entrée builds: au même fichier ko.yaml, avec les paramètres appropriés indiqués dans la documentation de Ko.

builds:
 - id: hello
   dir: .
   main: hello.go
   env:
     - GOOS=linux
     - CGO_ENABLED=0
   ldflags:
     - -extldflags "-static"
     - -linkmode external

Étiquettes Docker

Les étiquettes Docker, ou annotations OCI, permettent d’ajouter à une image des métadonnées utiles pour référencer des informations telles que le dépôt source d’origine, la compilation CI qui l’a créée ou toute autre donnée que votre équipe souhaite intégrer. Pour en savoir plus sur les étiquettes d’image et les cas où elles peuvent être utiles, consultez mon article Comment et quand utiliser les étiquettes Docker.

Ko permet d’ajouter des étiquettes avec l’option de ligne de commande --image-label :

$ ko build hello.go --image-label foo=bar -L –image-label org.opencontainers.image.source=https://repo.mycorp.com/superteam/hello
…
ko.local/hello.go-7a204cfb24536a350234d9132276cae7:acf1dce794131205d488ed8fe4818866ebf509a61f4cf60aa07463e2b054d97d

$ docker image inspect ko.local/hello.go-7a204cfb24536a350234d9132276cae7:latest --format '{{json .Config.Labels}}'
{"foo":"bar","org.opencontainers.image.source":"https://repo.mycorp.com/superteam/hello"}

Remarque : À la date de publication de cet article, il n’est pas possible de définir des étiquettes d’image dans le fichier de configuration .ko.yaml, mais une demande ouverte propose d’ajouter cette fonctionnalité.

Autres fonctionnalités intéressantes de Ko

SBOM des images

Comme nous l’avons vu plus haut, Ko génère et publie automatiquement une SBOM pour votre nouvelle image de conteneur. Par défaut, elle est au format SPDX, mais vous pouvez aussi choisir CycloneDX en ajoutant l’option --sbom=cyclonedx à la ligne de commande. Vous pouvez également désactiver cette fonctionnalité avec l’option --sbom=none. Vous trouverez ces paramètres et d’autres détails de configuration dans la documentation de Ko.

Une présentation détaillée des SBOM et de leur rôle dans la sécurisation de la chaîne d’approvisionnement dépasse le cadre de cet article. Pour en savoir plus, consultez Créer des SBOM pour sécuriser la chaîne d’approvisionnement open source.

Intégration à Kubernetes

Si vous devez tester votre image dans un cluster Kubernetes, vous avez certainement déjà rencontré les contraintes liées à la répétition de tâches telles que :

  • Créer mon image

  • La déployer dans mon registre (ou la charger d’une autre manière dans le cluster Kubernetes)

  • Mettre à jour le fichier YAML de mon déploiement avec le nouveau tag d’image

  • Redéployer avec kubectl

Ko simplifie considérablement ces opérations en les automatisant dans une seule commande : ko apply. Il suffit de remplacer l’étiquette de l’image dans le fichier YAML de votre déploiement par une valeur au format spécial ko://. ko apply effectuera alors automatiquement toutes les étapes ci-dessus et déploiera l’image sur le cluster actuellement sélectionné dans le contexte de configuration de votre kubectl.

Imaginons, par exemple, que vous disposiez d’un environnement de test ou d’un cluster Kubernetes local et que vous deviez déployer et tester votre travail au fur et à mesure. Voici un manifeste de déploiement pour notre application « hello » :

apiVersion: apps/v1
kind: Deployment
metadata:
 name: hello-server
spec:
 selector:
   matchLabels:
     run: hello-server
 replicas: 2
 template:
   metadata:
     labels:
       run: hello-server
   spec:
     containers:
       - name: hello-server
         imagePullPolicy: IfNotPresent
         image: ko://github.com/myteam/hello
         ports:
           - containerPort: 8080
             name: http

Vous pouvez voir que la ligne image: contient ko://, suivi du chemin vers le module Go de notre application sur GitHub.

Il ne nous reste plus qu’à exécuter ko apply en lui indiquant le fichier YAML avec l’option -f, comme nous le ferions avec kubectl :

$ ko apply -f myfile.yaml
2022/09/27 14:35:51 Using base gcr.io/distroless/static:nonroot@sha256:2a9e2b4fa771d31fe3346a873be845bfc2159695b9f90ca08e950497006ccc2e for github.com/myteam/hello
2022/09/27 14:35:51 Building github.myteam/hello for linux/amd64
…
2022/09/27 14:35:56 Published myteam/golang-ea0a77f5cbe6ba6aea599ad83048ae7b@sha256:bd7eb1052cf5b8664890f23683930f07423aba0089150bb818a53e76ef2ebf68
deployment.apps/hello-server created

$ k get pods
NAME                                READY   STATUS    RESTARTS   AGE
pod/hello-server-5b5cc95db4-gc9rn   1/1     Running   0          10s
pod/hello-server-5b5cc95db4-rhwnp   1/1     Running   0          10s

Nous pouvons maintenant tester notre application, la modifier et relancer ko apply -f myfile.yaml autant de fois que nécessaire. Ko se charge de tout le travail pour vous !

Comme vous pouvez vous y attendre, d’autres commandes Kubernetes pourraient également vous être utiles, notamment :

  • delete pour supprimer vos déploiements ; et

  • resolve pour générer du YAML que vous pouvez transmettre à kubectl ou à d’autres outils.

Consultez la https://github.com/ko-build/ko#kubernetes-integration pour accéder à la documentation complète.

Les défis liés à l’utilisation de Ko

Nous avons évoqué toutes les raisons qui pourraient vous inciter à adopter Ko dans vos workflows. Mais avant de vous lancer, parlons de certains défis que vous pourriez rencontrer en utilisant Ko.

Problématiques multiplateformes

Si vous développez sur une machine qui n’est pas basée sur Linux, l’absence d’un moteur de conteneurs peut compliquer quelque peu les choses. Une image de build basée sur un conteneur vous permet d’intégrer les outils de build ainsi que le système d’exploitation et ses bibliothèques dans une image de base commune, et de ne pas vous soucier du fait que la plateforme de votre poste de travail diffère peut-être de celle de votre environnement de production.

Prenons un exemple : la partie Go+Docker de l’exemple ci-dessus fonctionnera sur pratiquement toutes les plateformes, car, quel que soit l’endroit où vous effectuez le build, l’image de base golang fournit les bibliothèques Debian glibc nécessaires au compilateur Go pour créer le binaire statique. En revanche, si vous essayez d’exécuter l’exemple Ko avec les mêmes options ldflags sur une machine MacOS, vous obtiendrez des erreurs signalant l’absence de bibliothèques nécessaires aux liaisons statiques.

En effet, MacOS/Darwin ne comprend pas les bibliothèques nécessaires à la compilation croisée d’un binaire Linux statique (du moins au moment où j’écris ces lignes). Sans vouloir trop m’en prendre à Apple, des problèmes similaires peuvent se poser avec des architectures de processeur différentes sur les postes de travail Linux et Windows WLS.

Érosion des compétences

Les compétences et les connaissances nécessaires à la création et à la maintenance d’images de conteneurs bien conçues sont précieuses. L’utilisation d’outils qui en font abstraction ou les rendent inutiles peut réduire votre capacité à résoudre les problèmes en cas de défaillance. De fait, moins il y a de personnes dans une équipe qui maîtrisent les technologies de conteneurs, plus celle-ci dépend des personnes expérimentées pour le dépannage et la maintenance en cours d’exécution. Dans les cas extrêmes, cela peut entraîner un épuisement professionnel, des départs et/ou des pannes.

Relâchement de la vigilance en matière de sécurité

Si la création des images de conteneurs est entièrement automatisée et échappe au contrôle des développeurs, le respect de processus tels que l’analyse des vulnérabilités des images peut en pâtir. Des vulnérabilités peuvent s’introduire dans des images, des packages, des bibliothèques, etc. qui n’ont pas été mis à jour, exposant ainsi votre application aux attaques. Veillez donc à ce que ces analyses soient toujours effectuées par d’autres moyens automatisés, tels que :

  • Scripts de build ou Makefiles

  • Hooks Git

  • Étapes de build dans la CI

Si vous n’analysez pas encore vos images, Snyk propose gratuitement l’analyse des images de conteneurs pour détecter les vulnérabilités et vous fournir des conseils simples et pratiques pour y remédier.

Simplifier les images de conteneurs

Ko, comme d’autres outils de création d’images, peut considérablement simplifier la création d’images de conteneurs pour vos applications Go et aider vos équipes à rationaliser et à standardiser leur processus de création d’images. L’un des principaux avantages concerne vos outils de CI : vous n’avez plus besoin d’exposer un moteur de conteneurs à votre environnement de build, ce qui réduit considérablement les vecteurs d’attaque liés aux accès privilégiés dans votre SDLC. Que vous choisissiez ou non un outil de build comme Ko, veillez à ce que vos équipes maîtrisent les technologies de conteneurs ainsi que les risques de sécurité liés aux processus et aux outils utilisés pour créer leurs images.

Lancez-vous dans les compétitions Capture The Flag

Apprenez à résoudre des défis Capture The Flag en regardant à la demande notre atelier virtuel d’initiation.