Entre em alta: usando Docker + Go com MongoDB
Avalie esse Tutorial
Na comunidade de desenvolvedores, garantir que seus projetos sejam executados com precisão, independentemente do ambiente, pode ser difícil. Quer esteja tentando recriar uma demonstração a partir de um tutorial on-line ou trabalhando em uma revisão de código, ouvir as palavras "Well, it works on my machine… " pode ser muito desconcertante. Em vez de passar horas depurando, queremos apresentar a você uma plataforma que mudará sua experiência de desenvolvedor: Docker.
O Docker é uma ótima ferramenta para aprender, pois oferece aos desenvolvedores a capacidade de seus aplicativos serem usados facilmente entre ambientes e é eficiente em termos de recursos em comparação com as máquinas virtuais. Este tutorial irá orientá-lo sobre como navegar no Docker, além de como integrar oGo na plataforma. Usaremos esse projeto para nos conectar ao nosso MongoDB Atlas Search Cluster criado anteriormente para usar Sinônimos no Atlas Search. Fique atento para uma leitura bem-sucedida sobre como aprender todos os itens acima e, ao mesmo tempo, expandir seu conhecimento de gíries da Ger-Z com nosso cluster de sinônimos. Fique popular!
Há alguns requisitos que devem ser atendidos para ter sucesso com este tutorial.
- Cluster MongoDB Atlas AM0 ou superior
- Área de trabalho do Docker
Para usar o MongoDB com o driver Golang, você só precisa de um cluster M0 gratuito. Para criar esse cluster, siga as instruções listadas na documentação do MongoDB. No entanto, vamos fazer muitas referências a um tutorial anterior em que usamos o Atlas Search com sinônimos personalizados.
Como este é um tutorial Docker , você precisará Docker Desktop. Na verdade, você não precisa ter o Go configurado em sua máquina host porque o Docker pode lidar com isso para nós à medida que avançamos no tutorial.
Como mencionado anteriormente, você não precisa do Go instalado e configurado em seu computador host para ser bem-sucedido. No entanto, não faria mal usá-lo caso você queira testar as coisas antes de criar uma imagem do Docker.
Em seu computador, crie um novo diretório de projeto e, dentro desse diretório de projeto, crie um diretóriosrc com os seguintes arquivos:
- go.mod
- main.go
O arquivogo.mod é nosso arquivo de gerenciamento de dependência para módulos Go. Ele pode ser facilmente criado manualmente ou usando o seguinte comando:
1 go mod init
O arquivo principal.go é onde manteremos todo o código do nosso projeto.
Começando com o arquivogo.mod, adicione as seguintes linhas:
1 module github.com/mongodb-developer/docker-golang-example 2 go 1.15 3 require go.mongodb.org/mongo-driver v1.7.0 4 require github.com/gorilla/mux v1.8.0
Essenciais, estamos definindo qual versão do Go usar e os módulos que queremos usar. Para este projeto, usaremos o driver MongoDB Go, bem como oGorilla Web Toolkit.
Isso nos leva à construção de nossa API simples.
Dentro do arquivo main.go, adicione o seguinte código:
1 package main 2 3 import ( 4 "context" 5 "encoding/json" 6 "fmt" 7 "net/http" 8 "os" 9 "time" 10 11 "github.com/gorilla/mux" 12 "go.mongodb.org/mongo-driver/bson" 13 "go.mongodb.org/mongo-driver/mongo" 14 "go.mongodb.org/mongo-driver/mongo/options" 15 ) 16 17 var client *mongo.Client 18 var collection *mongo.Collection 19 20 type Tweet struct { 21 ID int64 `json:"_id,omitempty" bson:"_id,omitempty"` 22 FullText string `json:"full_text,omitempty" bson:"full_text,omitempty"` 23 User struct { 24 ScreenName string `json:"screen_name" bson:"screen_name"` 25 } `json:"user,omitempty" bson:"user,omitempty"` 26 } 27 28 func GetTweetsEndpoint(response http.ResponseWriter, request *http.Request) {} 29 func SearchTweetsEndpoint(response http.ResponseWriter, request *http.Request) {} 30 31 func main() { 32 fmt.Println("Starting the application...") 33 ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) 34 defer cancel() 35 client, err := mongo.Connect(ctx, options.Client().ApplyURI(os.Getenv("MONGODB_URI"))) 36 defer func() { 37 if err = client.Disconnect(ctx); err != nil { 38 panic(err) 39 } 40 }() 41 collection = client.Database("synonyms").Collection("tweets") 42 router := mux.NewRouter() 43 router.HandleFunc("/tweets", GetTweetsEndpoint).Methods("GET") 44 router.HandleFunc("/search", SearchTweetsEndpoint).Methods("GET") 45 http.ListenAndServe(":12345", router) 46 }
Há mais no código, mas, antes de vermos o restante, vamos começar a decompor o que temos acima para dar sentido a ele.
Você provavelmente notará nossa estrutura de dados
Tweets
:1 type Tweet struct { 2 ID int64 `json:"_id,omitempty" bson:"_id,omitempty"` 3 FullText string `json:"full_text,omitempty" bson:"full_text,omitempty"` 4 User struct { 5 ScreenName string `json:"screen_name" bson:"screen_name"` 6 } `json:"user,omitempty" bson:"user,omitempty"` 7 }
No início do tutorial, mencionamos que este exemplo é fortemente influenciada por um tutorial anterior que usou dados do Twitter. É altamente recomendável que você dê uma olhada nele. Essa estrutura de dados tem alguns dos campos que representam um tuíte que raspamos do Twitter. Não mapeamos todos os campos porque isso não era necessário para este exemplo.
Em seguida, você notará o seguinte:
1 func GetTweetsEndpoint(response http.ResponseWriter, request *http.Request) {} 2 func SearchTweetsEndpoint(response http.ResponseWriter, request *http.Request) {}
Essas serão as funções que manterão nossa lógica de endpoint da API. Vamos ignorá-los por enquanto e nos concentrarmos em entender a lógica de conexão e configuração.
Até o momento, a maior parte do que nos interessa está acontecendo na função
main
.A primeira coisa que estamos fazendo é conectar ao MongoDB:
1 ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) 2 defer cancel() 3 client, err := mongo.Connect(ctx, options.Client().ApplyURI(os.Getenv("MONGODB_URI"))) 4 defer func() { 5 if err = client.Disconnect(ctx); err != nil { 6 panic(err) 7 } 8 }() 9 collection = client.Database("synonyms").Collection("tweets")
Você provavelmente notará a
MONGODB_URI
variável de ambiente no código acima. Não é uma boa ideia codificar a MongoDB de conexão do string no aplicativo. Isso nos impede de sermos flexíveis e pode ser um risco à segurança. Em vez disso, estamos usando variáveis de ambiente que passaremos com o Docker quando implantarmos nossos contêineres.Você pode acessar o painel do MongoDB Atlas para sua string de URI .
O banco de dados que planejamos usar é
synonyms
e planejamos usar a collectiontweets
, sobre a qual mencionamos no tutorial anterior.Depois de nos conectar ao MongoDB, nos concentramos em configurar o Gorilla Web Toolkit:
1 router := mux.NewRouter() 2 router.HandleFunc("/tweets", GetTweetsEndpoint).Methods("GET") 3 router.HandleFunc("/search", SearchTweetsEndpoint).Methods("GET") 4 http.ListenAndServe(":12345", router)
Neste código, estamos definindo qual caminho do endpoint deve rotear para qual função. As funções estão definidas, mas ainda não adicionamos nenhuma lógica a elas. O próprio aplicativo servirá na porta 12345.
A partir de agora, o aplicativo possui as informações básicas de conexão e configuração necessárias. Vamos voltar a cada uma dessas funções de endpoint.
Começaremos com o
GetTweetsEndpoint
porque ele funcionará bem com um cluster M0 :1 func GetTweetsEndpoint(response http.ResponseWriter, request *http.Request) { 2 response.Header().Set("content-type", "application/json") 3 var tweets []Tweet 4 ctx, _ := context.WithTimeout(context.Background(), 30*time.Second) 5 cursor, err := collection.Find(ctx, bson.M{}) 6 if err != nil { 7 response.WriteHeader(http.StatusInternalServerError) 8 response.Write([]byte(`{ "message": "` + err.Error() + `" }`)) 9 return 10 } 11 if err = cursor.All(ctx, &tweets); err != nil { 12 response.WriteHeader(http.StatusInternalServerError) 13 response.Write([]byte(`{ "message": "` + err.Error() + `" }`)) 14 return 15 } 16 json.NewEncoder(response).Encode(tweets) 17 }
No código acima, estamos dizendo que queremos usar a operação
Find
em nossa coleção para todos os documentos nessa coleção, portanto, o objeto de filtro vazio.Se não houver erros, poderemos obter todos os resultados do nosso cursor, carregá-los em uma fatia
Tweet
e, em seguida, codificar JSON nessa fatia para enviar ao cliente. O cliente receberá dados JSON como resultado.Agora podemos examinar a função de endpoint mais interessante.
1 func SearchTweetsEndpoint(response http.ResponseWriter, request *http.Request) { 2 response.Header().Set("content-type", "application/json") 3 queryParams := request.URL.Query() 4 var tweets []Tweet 5 ctx, _ := context.WithTimeout(context.Background(), 30*time.Second) 6 searchStage := bson.D{ 7 {"$search", bson.D{ 8 {"index", "synsearch"}, 9 {"text", bson.D{ 10 {"query", queryParams.Get("q")}, 11 {"path", "full_text"}, 12 {"synonyms", "slang"}, 13 }}, 14 }}, 15 } 16 cursor, err := collection.Aggregate(ctx, mongo.Pipeline{searchStage}) 17 if err != nil { 18 response.WriteHeader(http.StatusInternalServerError) 19 response.Write([]byte(`{ "message": "` + err.Error() + `" }`)) 20 return 21 } 22 if err = cursor.All(ctx, &tweets); err != nil { 23 response.WriteHeader(http.StatusInternalServerError) 24 response.Write([]byte(`{ "message": "` + err.Error() + `" }`)) 25 return 26 } 27 json.NewEncoder(response).Encode(tweets) 28 }
A ideia por trás da função acima é que queremos usar um aggregation pipeline para o Atlas Search. Ela usa as informações de sinônimo que descrevemos no tutorial anterior.
A primeira coisa importante a ser observada no código acima é a seguinte:
1 queryParams := request.URL.Query()
Estamos obtendo os parâmetros de query passados com a solicitação HTTP. Estamos esperando que um parâmetro
q
exista com a query de pesquisa a ser usada.Para manter as coisas simples, usamos um único estágio para o pipeline de agregação do MongoDB:
1 searchStage := bson.D{ 2 {"$search", bson.D{ 3 {"index", "synsearch"}, 4 {"text", bson.D{ 5 {"query", queryParams.Get("q")}, 6 {"path", "full_text"}, 7 {"synonyms", "slang"}, 8 }}, 9 }}, 10 }
Neste estágio, estamos fazendo uma pesquisa de texto com um índice específico e um conjunto específico de sinônimos. A query que usamos para nossa pesquisa de texto vem do parâmetro query de nossa solicitação HTTP.
Supondo que tudo correu bem, podemos carregar todos os resultados do cursor em uma fatia
Tweet
, codificá-la em JSON e retorná-la ao cliente que a solicitou.Se você tiver o Go instalado e configurado em seu computador, vá em frente e tente executar esse aplicativo. Só não se esqueça de adicionar o
MONGODB_URI
às suas variáveis de ambiente antes.Se você quiser saber mais sobre o desenvolvimento de API com o Kit de Ferramentas da Web do Gorilla e o MongoDB, confira este tutorial sobre o assunto.
Vamos começar com o Docker! Se for uma plataforma que você nunca usou antes, pode parecer um pouco assustador no começo, mas deixe-nos guiá-lo passo a passo. Mostraremos como baixar o Docker e começar a configurar seu primeiro Dockerfile para se conectar ao nosso cluster Atlas de Sinônimos Gen-Z.
As primeiras coisas primeiro. Vamos baixar o Docker. Isso pode ser feito por meio do sitedeles em apenas alguns minutos.
Depois de colocar isso em funcionamento, é hora de criar seu primeiro Dockerfile.
Na raiz da sua pasta de projeto, crie um novo arquivo Dockerfile com o seguinte conteúdo:
1 #get a base image 2 FROM golang:1.16-buster 3 4 MAINTAINER anaiya raisinghani <anaiya.raisinghani@mongodb.com> 5 6 WORKDIR /go/src/app 7 COPY ./src . 8 9 RUN go get -d -v 10 RUN go build -v 11 12 CMD ["./docker-golang-example"]
Muitos Dockerfiles são compostos desse formato, e muitos deles são altamente personalizáveis e podem ser editados para atender às necessidades do seu projeto.
O primeiro passo é pegar uma imagem base que você vai usar para construir sua nova imagem. Você pode pensar em usar Dockerfiles como camadas de um bolo. Há uma variedade de imagens de base diferentes por exemplo, ou você pode usar
FROM scratch
para começar a partir de uma imagem totalmente em branco. Como esse projeto está usando a linguagem de programação Go, optamos por começar da imagem basegolang
e adicionar a marcação 1.16
para representar a versão do Go que planejamos usar. Sempre que você incluir uma marcação ao lado da imagem base, certifique-se de configurá-la com dois pontos no meio, assim: golang:1.16
. Para saber mais sobre qual tag beneficiará mais seu projeto, consulte a documentação do Docker sobre o assunto.Este site contém muitas tags diferentes que podem ser usadas em uma imagem base do Golang. As tags são importantes porque contêm informações muito valiosas sobre a imagem base que você está usando, como versões de software, variações do sistema operacional etc.
Vamos contar o resto do que vai acontecer neste Dockerfile!
É opcional incluir um
MAINTAINER
para sua imagem, mas é uma boa prática para que as pessoas que visualizam seu Dockerfile possam saber quem o criou. Não é necessário, mas é útil incluir seu nome completo e seu endereço de e-mail no arquivo. É crucial incluir o comando em seu Dockerfile, pois {
WORKDIR /go/src/app
WORKDIR
especifica em qual diretório de trabalho você está. Todos os comandos a seguir serão executados em qualquer diretório que você escolher, portanto, certifique-se de estar ciente de em qual diretório você está atualmente em.O comando
COPY ./src .
permite a você copiar quaisquer arquivos que você desejar do local especificado na máquina host para a imagem Docker.Agora, podemos usar o comando
RUN
para definir exatamente o que queremos que aconteça no momento da construção da imagem antes da implantação como container. O primeiro comando que temos é RUN go get -d -v
, que baixará todas as dependências do Go listadas no arquivogo.mod que foi copiado na imagem.Nosso segundo comando
RUN
éRUN go build -v
, que construirá nosso projeto em um arquivo binário executável.A última etapa deste Dockerfile é usar um comando
CMD
, CMD [“./docker-golang-example”]
. Este comando definirá o que é executado quando o container é implantado e não quando a imagem é construída. Essenciais, estamos dizendo que queremos que o aplicativo Go construído seja executado quando o contêiner for implantado.Depois de configurar o Dockerfile, você poderá criar e executar seu projeto usando todo o link da URI do MongoDB:
Para construir a imagem Docker e implantar o contêiner, execute o seguinte a partir da linha de comando:
1 docker build -t docker-syn-image . 2 docker run -d -p 12345:12345 -e “MONGODB_URI=YOUR_URI_HERE” docker-syn-image
Siga estas instruções permitirá que você execute o projeto e acessá-lo a partir de http://localhost:12345. Mas! É tão entediante. E se nós lhe dissermos que há uma maneira mais fácil de executar seu aplicativo sem ter que escrever todo o link URI? Há! Só é preciso uma etapa a mais: configurar um arquivo Docker Compose.
Um arquivo Docker Compose é uma pequena etapa legal para executar todos os seus arquivos e dependências do container por meio de um comando simples:
docker compose up
.Para configurar este arquivo, você precisa estabelecer um arquivo de configuração YAML primeiro. Faça isso criando um novo arquivo na raiz da pasta do projeto, nomeando-o docker-composee adicionando .yml no final. Você pode nomear outra coisa se quiser, mas esta é a mais fácil, pois ao executar o comando
docker compose up
, você não precisará especificar um nome de arquivo. Quando estiver na pasta do projeto, siga as etapas abaixo.É assim que seu arquivo Docker Compose ficará depois de configurar tudo:
1 version: "3.9" 2 services: 3 web: 4 build: . 5 ports: 6 - "12345:12345" 7 environment: 8 MONGODB_URI: your_URI_here
Vamos percorrer isso!
Primeiras coisas primeiro. Determine qual versão do esquema você deseja executar. Você deve estar usando a versão mais recente e pode descobrir isso na documentação do Docker.
Em seguida, defina quais serviços, também conhecidos como container, você deseja executar em seu projeto. Incluímos
web
desde que estamos anexando ao nosso Atlas Search. O nome não é importante e atua mais como um identificador desse serviço específico. Em seguida, especifique que você está criando seu aplicativo e insira as informaçõesports
no local correto. Para a próxima etapa, podemos configurar nosso environment
como nosso MongoDB URI e pronto!Agora, execute o comando
docker compose up
e veja a mágica acontecer. Seu container deverá ser compilado e executado, e você poderá se conectar à sua porta e ver todos os tweets!Este tutorial agora deixou você preparado com o conhecimento necessário para criar uma API Go com o driver Golang do MongoDB, criar um Dockerfile, criar um arquivo Docker Compose e conectar seu container recém-construído a um MongoDB Atlas Cluster.
O uso dessas novas plataformas permitirá que você leve seus projetos a um nível totalmente novo.