Heim >Backend-Entwicklung >Golang >Go + Docker: So erstellen Sie die besten Docker-Images für Golang-Anwendungen

Go + Docker: So erstellen Sie die besten Docker-Images für Golang-Anwendungen

WBOY
WBOYOriginal
2024-08-14 22:49:02452Durchsuche

Wer hatte noch nie Zweifel daran, ob er das richtige Docker-Image für seine Anwendung erstellt hat? Nun, diese Frage hatte ich schon mehrmals und ich wusste fast immer nicht, ob ich es richtig oder falsch gemacht habe.

In diesem Artikel werden wir daher fortgeschrittene Vorgehensweisen zum Erstellen effizienter und optimierter Docker-Images für Ihre Go-Anwendungen untersuchen. Wir werden verschiedene Ansätze vergleichen, z. B. die Verwendung der Alpine- und Scratch-Base-Images, und die Vorteile jedes einzelnen anhand des Codes diskutieren Beispiele und Leistungsanalysen.

Projektstruktur

Lassen Sie uns zunächst eine typische Projektstruktur für eine containerisierte Go-Anwendung erstellen.

Als Beispiel verwende ich diese Anwendung, die ein URL-Shortener ist:

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. Schreiben der Docker-Datei

Was nur wenige Menschen wissen ist, dass wir kein „vollständiges“ Image benötigen, das in der Produktion läuft. Zum Beispiel Ubuntu mit allen Paketen, Quellen, Erweiterungen und einem SDK für unsere Sprache. Wir können unsere App einfach innerhalb eines Systems mit einem SDK erstellen und den Build dann in ein kleineres, optimiertes Image kopieren, das nur diesen Build ausführt. Und hier kommt Multi-Stage ins Spiel.

Mehrstufige Builds

In der Docker-Datei können Sie mehrere Build-Stufen definieren, die jeweils mit einer FROM-Anweisung beginnen. Die erste Stufe kann zum Kompilieren des Codes, zum Installieren von Abhängigkeiten, zum Ausführen von Tests usw. verwendet werden. In nachfolgenden Schritten können Sie nur die notwendigen Artefakte (z. B. kompilierte Binärdateien) in das endgültige Image kopieren und alles verwerfen, was nicht zum Ausführen der Anwendung benötigt wird.

# 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"]

Verdammter Rafa, aber was machst du mit „diesen beiden Bildern“? Zuerst verwende ich ein Image als Builder, in dem wir die ausführbare Datei der Anwendung erstellen:

Erste Phase (Bauphase):

  • Verwendet golang:1.22-alpine als Basisbild.
  • Kopiert den Quellcode in den Container.
  • Laden Sie die Abhängigkeiten herunter und kompilieren Sie die Go-Anwendungsbinärdatei.

Im Moment verwende ich ein kleineres Image, das nur diese ausführbare Datei ausführt, die wir im ersten Schritt generiert haben:

Zweite Phase (Endphase):

  • Verwendet alpine:latest als Basis-Image, das viel kleiner ist und nur über die notwendigen Tools zum Ausführen der Binärdatei verfügen muss.
  • Kopiert die kompilierte Binärdatei aus der vorherigen Stufe (Builder) in das neue Image.
  • Definiert den Anwendungsausführungsbefehl.

Wenn wir bestätigen möchten, was wirklich passiert, gibt es in Docker Desktop die Möglichkeit, die Hierarchie eines Bildes zu analysieren. Im Inneren können wir sehen, was er verwendet:

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

Jetzt können wir auch die Größe dieses gerade generierten Bildes analysieren, in diesem Fall des URL-Shorteners:alpine, der ~30 MB betrug:

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

Aber was ist Alpin?

Alpine ist eine minimalistische, sichere und leichte Linux-Distribution, die aufgrund ihrer Effizienz und Einfachheit häufig in Containerumgebungen verwendet wird. Es bietet eine solide Grundlage für die Erstellung skalierbarer Anwendungen ohne den Mehraufwand anderer schwererer Linux-Distributionen.

Einige der Vorteile der Nutzung von Alpine in unserer App hängen hauptsächlich mit diesen drei Säulen zusammen:

  • Flexibilität: Das Alpine-Image (~5 MB) ist klein und enthält einen APK-Paketmanager, mit dem Sie zusätzliche erforderliche Abhängigkeiten installieren können.
  • Kompatibilität: Unterstützung für dynamische Bibliotheken, wodurch es für Anwendungen geeignet ist, die auf externe Bibliotheken angewiesen sind.
  • Sicherheit: Alpine wird regelmäßig aktualisiert und enthält Sicherheitspatches, die das Risiko von Schwachstellen verringern.

Okay, aber was ist, wenn ich dieselbe SDK-Version verwende, in diesem Fall golang:1.22-alpine? Wie groß ist meine Bewerbung?

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

Nun, in diesem Fall haben wir am Ende ein Bild mit ~250 MB erhalten ... Im Vergleich zum alpinen Bild sind wir zwar nur auf ~30 MB gegangen, aber das ist schon ein großer Unterschied. Und lässt es sich noch weiter verbessern?

Die Antwort ist JA, und jetzt gehen wir auf die Details ein

2. Erweiterte Optimierungen

2.1 Kratzer

Scratch ist ein besonderes und sehr minimalistisches Bild in Docker. Es ist tatsächlich das einfachste und leerste Basisbild, das Sie verwenden können. Es enthält absolut nichts: kein Betriebssystem, keine Bibliotheken, keine Tools – es ist buchstäblich ein leerer Container.

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.

Praxisbeispiel: Verwendung von Docker Scout auf Docker Desktop

  1. Zugriff auf Docker Desktop: Öffnen Sie Docker Desktop und gehen Sie zur Registerkarte „Bilder“. Hier sehen Sie eine Liste aller Bilder, die Sie lokal haben. In diesem Beispiel habe ich das Bild postgres:16-alpine verwendet, da es Schwachstellen enthält, die wir als Beispiel verwenden können. Go + Docker: Como criar as melhores imagens Docker para aplicações Golang
  2. Führen Sie eine Schwachstellenanalyse durch: Wählen Sie ein Bild aus, das Sie analysieren möchten. Docker Scout zeigt automatisch bekannte Schwachstellen im ausgewählten Image an. Neben jedem Bild wird ein Sicherheitsstatussymbol angezeigt, das angibt, ob Schwachstellen behoben werden müssen. Go + Docker: Como criar as melhores imagens Docker para aplicações Golang
  3. Details zur Sicherheitslücke anzeigen: Klicken Sie auf das Bild, um ein detailliertes Dashboard der gefundenen Schwachstellen anzuzeigen. Dazu gehören die betroffene Paketversion, die CVE-Beschreibung und der Schweregrad. Sie können auch den Verlauf von Scans und Änderungen anzeigen und so verfolgen, wie sich die Bildsicherheit im Laufe der Zeit entwickelt hat. Go + Docker: Como criar as melhores imagens Docker para aplicações Golang
  4. Korrekturen anwenden: Basierend auf den Empfehlungen von Docker Scout können Sie entscheiden, Pakete zu aktualisieren, das Image mit einer sichereren Basis neu zu erstellen oder andere Abhilfemaßnahmen zu ergreifen. Korrekturen können direkt auf Ihre Dockerfile- oder CI/CD-Pipeline angewendet werden. Go + Docker: Como criar as melhores imagens Docker para aplicações Golang

Und auf diese Weise verfügen wir über eine proaktive Prävention, die Schwachstellen identifiziert und behebt, bevor das Image in der Produktion bereitgestellt wird, uns vor möglichen Sicherheitslücken schützt und außerdem die betriebliche Effizienz steigert. Was meine ich damit? Wir können die Schwachstellenanalyse automatisieren, sodass sich das DevOps- oder Sicherheitsteam auf Korrekturmaßnahmen statt auf manuelle Untersuchungen konzentrieren kann.

Abschluss

Mit den von uns untersuchten Vorgehensweisen haben Sie jetzt einen klaren Weg zum Erstellen von Docker-Images für Ihre Go-Anwendungen. Verwenden Sie Techniken wie Multi-Stage Builds, die die Image-Größe reduzieren, und wählen Sie Basis-Images wie Alpine oder Scratch aus, um die Sicherheit zu verbessern Effizienz und durch die Verwendung von Docker Scout zur Überwachung von Schwachstellen können Sie sicherstellen, dass Ihre Anwendungen effizient und sicher ausgeführt werden.

Diese Praktiken verbessern nicht nur die technische Leistung, sondern bringen auch direkte Vorteile für Ihr tägliches Leben und das Unternehmen und sparen Zeit und Ressourcen.

Wenn Sie also das nächste Mal ein Docker-Image erstellen, sollten Sie diese Strategien im Hinterkopf behalten. Wenden Sie sie an und beobachten Sie die Ergebnisse. ?

Das obige ist der detaillierte Inhalt vonGo + Docker: So erstellen Sie die besten Docker-Images für Golang-Anwendungen. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn
Vorheriger Artikel:So verwenden Sie Go MongoNächster Artikel:So verwenden Sie Go Mongo