Maison  >  Article  >  développement back-end  >  Go + Docker : Comment créer les meilleures images Docker pour les applications Golang

Go + Docker : Comment créer les meilleures images Docker pour les applications Golang

WBOY
WBOYoriginal
2024-08-14 22:49:02435parcourir

Qui n’a jamais douté de la création de la bonne image Docker pour son application ? Eh bien, j'ai posé cette question plusieurs fois et je ne savais presque toujours pas si je la faisais bien ou mal.

Dans cet article, nous explorerons des pratiques avancées pour créer des images Docker efficaces et optimisées pour vos applications Go. Nous comparerons différentes approches, telles que l'utilisation des images de base alpines et scratch, et discuterons des avantages de chacune, avec du code. exemples et analyses de performances.

Structure du projet

Tout d'abord, établissons une structure de projet typique pour une application Go conteneurisée.

A titre d'exemple, j'utilise cette application qui est un raccourcisseur d'URL :

url_shortener
├── cmd
│   └── main.go
├── internal
│   ├── monitoring
│   │   └── prometheus.go
│   ├── server
│   │   └── server.go
│   └── shortener
│       ├── model.go
│       ├── repository.go
│       ├── service.go
│       └── service_test.go
├── pkg
│   ├── api
│   │   └── shortener
│   │       ├── handler.go
│   │       └── handler_integration_test.go
│   └── utils
│       └── base62
│           ├── hash.go
│           └── hash_test.go
├── Dockerfile
├── Dockerfile.alpine
├── Dockerfile.golang
├── README.md
├── compose.yml
├── go.mod
├── go.sum
└── prometheus.yml

1. Écriture du fichier Docker

Ce que peu de gens savent, c'est que nous n'avons pas besoin d'une image « complète » en production. Par exemple, Ubuntu avec tous les packages, sources, extensions et un SDK pour notre langage. Nous pouvons simplement créer notre application dans un système avec un SDK, puis copier la version dans une image plus petite et optimisée qui exécutera uniquement cette version. Et c'est là qu'intervient le Multi-Stage.

Constructions en plusieurs étapes

Dans le Dockerfile, vous pouvez définir plusieurs étapes de construction, chacune commençant par une instruction FROM. La première étape peut être utilisée pour compiler le code, installer des dépendances, exécuter des tests, etc. Dans les étapes suivantes, vous pouvez copier uniquement les artefacts nécessaires (tels que les binaires compilés) dans l'image finale, en supprimant tout ce qui n'est pas nécessaire pour exécuter l'application.

# syntax=docker/dockerfile:1

# Build stage
FROM golang:1.22-alpine AS builder
WORKDIR /app
COPY . .
RUN go mod download
RUN go build -o url-shortener ./cmd

# Final stage
FROM alpine:latest
WORKDIR /app
COPY --from=builder /app/url-shortener .
CMD ["./url-shortener"]

Merde Rafa, mais qu'est-ce que tu fais avec "ces deux images" ? J'utilise d'abord une image comme constructeur, c'est là que nous créons l'exécutable de l'application :

Première étape (Étape de construction) :

  • Utilise golang:1.22-alpine comme image de base.
  • Copie le code source dans le conteneur.
  • Téléchargez les dépendances et compilez le binaire de l'application Go.

Dans ce cas, j'utilise une image plus petite qui exécutera simplement cet exécutable que nous avons généré dans la première étape :

Deuxième étape (Étape finale) :

  • Utilise alpine:latest comme image de base, qui est beaucoup plus petite et n'a besoin que des outils nécessaires pour exécuter le binaire.
  • Copie le binaire compilé de l'étape précédente (constructeur) vers la nouvelle image.
  • Définit la commande d'exécution de l'application.

Si nous voulons confirmer ce qui se passe réellement, dans Docker Desktop il y a la possibilité d'analyser la hiérarchie d'une image. A l'intérieur on peut voir ce qu'il utilise :

Go + Docker: Como criar as melhores imagens Docker para aplicações Golang

Maintenant, nous pouvons également analyser la taille de cette image que nous venons de générer, dans ce cas l'url-shortener:alpine qui faisait ~30 Mo :

$ docker images
REPOSITORY            TAG         IMAGE ID       CREATED              SIZE
url-shortener                 alpine          aa99d6a2c028   3 minutes ago    29.9MB

Mais qu’est-ce qu’Alpine ?

Alpine est une distribution Linux minimaliste, sécurisée et légère, largement utilisée dans les environnements de conteneurs en raison de son efficacité et de sa simplicité. Il fournit une base solide pour créer des applications évolutives sans les frais généraux d'autres distributions Linux plus lourdes.

Certains des avantages de l'utilisation d'Alpine dans notre application sont principalement liés à ces 3 piliers :

  • Flexibilité : L'image alpine (~ 5 Mo) est petite et comprend un gestionnaire de packages apk, vous permettant d'installer des dépendances supplémentaires requises.
  • Compatibilité : Prise en charge des bibliothèques dynamiques, ce qui le rend adapté aux applications qui dépendent de bibliothèques externes.
  • Sécurité : Régulièrement mis à jour, alpine inclut des correctifs de sécurité, réduisant les risques de vulnérabilités.

D'accord, mais que se passe-t-il si j'utilise la même version du SDK, dans ce cas golang:1.22-alpine. Quelle est la taille de ma candidature ?

REPOSITORY                    TAG             IMAGE ID       CREATED         SIZE
url-shortener                 golang-alpine   d615d75c3aff   25 minutes ago   251MB

Eh bien, dans ce cas on s'est retrouvé avec une image avec ~250mo... Alors que par rapport à l'alpin, on est passé purement à ~30mo, c'est déjà une grosse différence. Et peut-il être encore amélioré ?

La réponse est OUI, et entrons dans les détails

2. Optimisations avancées

2.1 Rayure

Scratch est une image spéciale et très minimaliste dans Docker. Il s'agit en fait de l'image de base la plus simple et la plus vide possible que vous puissiez utiliser. Il ne contient absolument rien : pas de système d'exploitation, pas de bibliothèques, pas d'outils — c'est littéralement un conteneur vide.

Essa abordagem minimalista traz benefícios significativos, especialmente em termos de segurança. Ao usar Scratch, você minimiza drasticamente a superfície de ataque, já que não há pacotes ou ferramentas adicionais que possam introduzir vulnerabilidades. Seu contêiner contém apenas o essencial para a execução do aplicativo, garantindo um ambiente imutável e previsível em qualquer situação.

# syntax=docker/dockerfile:1

# Build stage
FROM golang:1.22-alpine AS builder
WORKDIR /app
COPY . .
RUN go mod download
RUN go build -o url-shortener ./cmd

# Final stage with Scratch
FROM scratch
WORKDIR /app
COPY --from=builder /app/url-shortener .
CMD ["./url-shortener"]

E o resultado após criar as 3 imagens, nosso ranking de menor imagem ficou assim:

  1. url-shortener:scratch - 21.1MB
  2. url-shortener:alpine - 29.9MB
  3. url-shortener:golang-alpine - 251MB

O Scratch conseguiu baixar mais alguns megas no tamanho final de nossa aplicação. Isso impacta no tamanho de arquivos transferidos pela rede, hoje algumas empresas cobram pela banda que trafegamos dentro dos servidores, e também pode influenciar em nosso Horizontal Scaling da aplicação.

Deixei os 3 Dockerfiles dentro do repositório do github caso você queira testar em seu próprio pc ?

Quando devo usar o scratch

A resposta mais tranquila para essa é "quase sempre", uma dica de cara é: ele vai muito bem com linguagens como Go, Rust, ou C/C++. Mas qui estão alguns pontos para levar em consideração na hora de escolher se deve ou não usar o scratch:

  • Aplicações Statically Linked: Se sua aplicação não depende de bibliotecas dinâmicas e pode ser compilada de forma estática, Scratch é uma excelente escolha.
  • Segurança: Quando a segurança é uma prioridade e você quer minimizar a quantidade de software no contêiner.
  • Eficiência: Para criar imagens Docker extremamente pequenas e eficientes.

2.2 Reduzindo o Tempo de Build (Cache)

Usar o cache do Docker para otimizar o tempo de build é uma técnica essencial para evitar recompilar ou baixar dependências desnecessariamente em cada build. O Docker armazena em cache as camadas de cada etapa do Dockerfile, reutilizando-as sempre que possível.

Em projetos Go, baixar dependências com go mod download pode ser um processo demorado, especialmente se houver muitas dependências. Se você recompilar todas as dependências em cada build, isso aumenta significativamente o tempo de build.

Como arrumar isso?

Ao copiar apenas os arquivos go.mod e go.sum em uma etapa separada antes de copiar o código-fonte completo, você permite que o Docker use o cache dessa etapa se os arquivos go.mod e go.sum não tiverem mudado. Veja como fica nosso Docker file com as mudanças:

# syntax=docker/dockerfile:1

# Build stage
FROM golang:1.22-alpine AS builder
WORKDIR /app

COPY go.mod go.sum ./
RUN go mod download

COPY . .
RUN go build -o url-shortener ./cmd

# Final stage
FROM scratch
WORKDIR /app
COPY --from=builder /app/url-shortener .
CMD ["./url-shortener"]

Só de fazer esta pequena mudança já ganhamos dois pontos bem interessantes quando se trata de desenvolvimento de software, que são:
Menor tempo de build: Se não houver alterações nos arquivos go.mod e go.sum, o Docker reutiliza o cache, evitando o download das dependências e economizando tempo.
Eficiência no CI/CD: Em pipelines de integração contínua, essa técnica reduz o tempo de execução dos pipelines, aumentando a eficiência do desenvolvimento e entrega.

Então bora usar isso a nosso favor no dia-a-dia :)

2.3 Melhorando a Segurança (Docker Scout)

Docker Scout é uma ferramenta da Docker integrada ao Docker Desktop que analisa suas imagens para identificar vulnerabilidades de segurança. Ele fornece insights sobre as dependências presentes em suas imagens e alerta sobre possíveis problemas, permitindo que você tome medidas corretivas antes de implantar suas aplicações.

Por que é importante? Manter suas imagens Docker seguras é fundamental para proteger suas aplicações contra ataques e exploração de vulnerabilidades. O Docker Scout automatiza o processo de análise, tornando mais fácil manter suas imagens seguras e atualizadas.

Como funciona?

O Scout funciona praticamente com 2 passos, ele examina a imagem Docker, mapeia todas as dependências incluídas na imagem e verifica essas dependências em uma base de dados de vulnerabilidades conhecidas. Por fim, classifica as vulnerabilidades encontradas por severidade e fornece recomendações para corrigir ou mitigar os problemas.

Exemple pratique : utilisation de Docker Scout sur Docker Desktop

  1. Accédez à Docker Desktop : Ouvrez Docker Desktop et accédez à l'onglet "Images". Ici, vous verrez une liste de toutes les images que vous avez localement. Dans cet exemple, j'ai utilisé l'image postgres:16-alpine, car elle contient des vulnérabilités que nous pouvons utiliser comme exemple. Go + Docker: Como criar as melhores imagens Docker para aplicações Golang
  2. Effectuer une analyse de vulnérabilité : Sélectionnez une image que vous souhaitez analyser. Docker Scout affichera automatiquement les vulnérabilités connues dans l'image sélectionnée. Vous verrez une icône d'état de sécurité à côté de chaque image, indiquant s'il existe des vulnérabilités à corriger. Go + Docker: Como criar as melhores imagens Docker para aplicações Golang
  3. Afficher les détails de la vulnérabilité : Cliquez sur l'image pour voir un tableau de bord détaillé des vulnérabilités trouvées. Cela inclut la version du package concerné, la description du CVE et la gravité. Vous pouvez également consulter l'historique des analyses et des modifications, ce qui vous aide à suivre l'évolution de la sécurité des images au fil du temps. Go + Docker: Como criar as melhores imagens Docker para aplicações Golang
  4. Appliquer les correctifs : Sur la base des recommandations de Docker Scout, vous pouvez décider de mettre à jour les packages, de reconstruire l'image avec une base plus sécurisée ou de prendre d'autres mesures d'atténuation. Les correctifs peuvent être appliqués directement à votre pipeline Dockerfile ou CI/CD. Go + Docker: Como criar as melhores imagens Docker para aplicações Golang

Et de cette façon, nous aurons une prévention proactive qui identifiera et corrigera les vulnérabilités avant que l'image ne soit déployée en production, aidera à se protéger contre d'éventuels exploits de sécurité et nous gagnerons également en efficacité opérationnelle, qu'est-ce que je veux dire par là ? Nous pouvons automatiser l'analyse des vulnérabilités, permettant ainsi à l'équipe DevOps ou de sécurité de se concentrer sur les actions correctives plutôt que sur les enquêtes manuelles.

Conclusion

Grâce aux pratiques que nous avons explorées, vous disposez désormais d'un chemin clair pour créer des images Docker pour vos applications Go en utilisant des techniques telles que les constructions en plusieurs étapes, qui réduisent la taille de l'image, en choisissant des images de base comme alpines ou scratch pour améliorer la sécurité et. efficacité et en utilisant Docker Scout pour surveiller les vulnérabilités, vous pouvez vous assurer que vos applications fonctionnent efficacement et en toute sécurité.

Ces pratiques améliorent non seulement les performances techniques, mais apportent également des bénéfices directs à votre vie quotidienne et à l'entreprise, en économisant du temps et des ressources.

Donc, la prochaine fois que vous créerez une image Docker, gardez ces stratégies à l'esprit. Appliquez-les et observez les résultats. ?

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Article précédent:Comment utiliser Go MongoArticle suivant:Comment utiliser Go Mongo