Heim > Artikel > Backend-Entwicklung > Go + Docker: So erstellen Sie die besten Docker-Images für Golang-Anwendungen
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.
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
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.
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):
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):
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:
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
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:
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
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:
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 ?
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:
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.
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 :)
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.
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.
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.
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!