Em minha série de desenvolvimento sobre Docker Já falei sobre a implantação de aplicativos Java e Node.js como contêineres junto com os contêineres do Couchbase Server. Desta vez, achei que seria legal implantar um aplicativo da Web Golang como um contêiner do Docker junto com Couchbase.
O processo é muito semelhante ao que eu já havia demonstrado nos tutoriais anteriores, mas vale a pena explorá-lo. Vamos explorar a conteinerização de um aplicativo que expliquei em um tutorial anterior sobre o tópico de serviços de encurtamento de URL.
Se estiver interessado, você pode conferir Use o Docker para implantar um aplicativo da Web Java em contêiner com Couchbase e Implantar um aplicativo da Web Node.js com Couchbase como contêineres do Dockerdependendo de suas preferências de idioma.
Os requisitos
Há apenas uma dependência que deve ser atendida para que este guia seja bem-sucedido. Você precisa do Docker instalado em um computador host. Você não precisa ter o Golang instalado nem o Couchbase Server, pois essa é a beleza do Docker Engine.
Embora não seja necessário, eu recomendaria que você se familiarizasse com o aplicativo em questão. Para saber mais sobre o código por trás do aplicativo, confira o tutorial que escrevi chamado, Criar um encurtador de URL com Golang e Couchbase NoSQL.
Estabelecimento de um projeto Docker para o aplicativo Golang
O código-fonte do aplicativo e o que estaremos empacotando podem ser vistos abaixo. Você deve salvá-lo como um arquivo de código-fonte Go, como main.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 |
pacote principal importação ( "encoding/json" "log" "net/http" "os" "tempo" "github.com/couchbase/gocb" "github.com/gorilla/mux" "github.com/speps/go-hashids" ) type MyUrl struct { ID string `json: "id,omitempty"` LongUrl string `json: "longUrl,omitempty"` ShortUrl string `json: "shortUrl,omitempty"` } var bucket *gocb.Bucket var bucketName string func ExpandEndpoint(w http.ResponseWriter, req *http.Request) { var n1qlParams []interface{} query := gocb.NewN1qlQuery("SELECT `" + bucketName + "`.* FROM `" + bucketName + "` WHERE shortUrl = $1") params := req.URL.Query() n1qlParams = append(n1qlParams, params.Get("shortUrl")) rows, _ := bucket.ExecuteN1qlQuery(query, n1qlParams) var row MyUrl rows.One(&row) json.NewEncoder(w).Encode(row) } func CreateEndpoint(w http.ResponseWriter, req *http.Request) { var url MyUrl _ = json.NewDecoder(req.Body).Decode(&url) var n1qlParams []interface{} n1qlParams = append(n1qlParams, url.LongUrl) query := gocb.NewN1qlQuery("SELECT `" + bucketName + "`.* FROM `" + bucketName + "` WHERE longUrl = $1") rows, err := bucket.ExecuteN1qlQuery(query, n1qlParams) if err != nil { w.WriteHeader(401) w.Write([]byte(err.Error())) retorno } var row MyUrl rows.One(&row) se row == (MyUrl{}) { hd := hashids.NewData() h := hashids.NewWithData(hd) now := time.Now() url.ID, _ = h.Encode([]int{int(now.Unix())}) url.ShortUrl = "http://localhost:12345/" + url.ID bucket.Insert(url.ID, url, 0) } else { url = linha } json.NewEncoder(w).Encode(url) } func RootEndpoint(w http.ResponseWriter, req *http.Request) { params := mux.Vars(req) var url MyUrl bucket.Get(params["id"], &url) http.Redirect(w, req, url.LongUrl, 301) } func main() { roteador := mux.NewRouter() cluster, _ := gocb.Connect("couchbase://" + os.Getenv("COUCHBASE_HOST")) bucketName = os.Getenv("COUCHBASE_BUCKET") bucket, _ = cluster.OpenBucket(bucketName, "") roteador.HandleFunc("/{id}", RootEndpoint).Methods("GET") roteador.HandleFunc("/expand/", ExpandEndpoint).Methods("GET") router.HandleFunc("/create", CreateEndpoint).Methods("PUT") log.Fatal(http.ListenAndServe(":12345", router)) } |
A única diferença entre o código acima e o tutorial anterior sobre o assunto é o uso do parâmetro os.Getenv("COUCHBASE_HOST")
e os.Getenv("COUCHBASE_BUCKET")
comandos. Isso nos permitirá definir as informações de conexão do Couchbase no tempo de execução do contêiner por meio de variáveis de ambiente, em vez de codificá-las no aplicativo.
Se você tivesse o Couchbase Server em execução, poderia iniciar o aplicativo da Web fazendo isso:
1 |
env COUCHBASE_HOST=localhost COUCHBASE_BUCKET=default go run *.go |
É claro que o projeto precisaria estar em seu $GOPATHmas seria acessível a partir de http://localhost:12345 conforme definido no código.
O objetivo aqui não é executar esse projeto localmente, mas por meio de um contêiner do Docker. Crie um contêiner Dockerfile ao lado do arquivo main.go ou como você o chamou. O arquivo Dockerfile deve conter o seguinte:
1 2 3 4 5 6 7 8 9 10 11 12 |
DE golang:alpine Execute o apk update && apk add git COPY . /go/src/app/ WORKDIR /go/src/app EXECUTAR go get -d -v EXECUTAR go install -v CMD ["app"] |
Como base, usaremos a imagem oficial do Golang Alpine Linux para o Docker, mas a personalizaremos. Durante o tempo de compilação, estamos instalando o Git, copiando o arquivo main.go à imagem, instalando as dependências do projeto encontradas no arquivo importação
e instalando o aplicativo. No tempo de execução, estamos executando o aplicativo instalado.
Para criar uma imagem a partir de nosso blueprint personalizado do Docker, executaríamos o seguinte:
1 |
docker build -t golang-project . |
Depois que todas as etapas de tempo de compilação forem concluídas, ficaremos com um projeto golang
Imagem do Docker. Antes de tentarmos executar essa imagem, precisamos nos preocupar com o Couchbase. Embora pudéssemos executar o Couchbase fora de um contêiner, qual é a graça disso?
Criação de uma imagem personalizada do Docker do Couchbase Server
Assim como no caso do Golang, existe uma imagem oficial do Docker para o Servidor Couchbase. Embora seja uma solução perfeitamente aceitável, não é uma solução automatizada. Isso significa que você terá de configurar manualmente o banco de dados depois que o contêiner for iniciado. Provavelmente, queremos evitar isso.
Em vez disso, crie um diretório em algum lugar do seu computador com um Dockerfile arquivo e configure.sh nele. O plano é criar um blueprint do Docker que executará o script de configuração para nós.
Abra o Dockerfile e inclua o seguinte:
1 2 3 4 5 |
DE couchbase COPIAR configure.sh /opt/couchbase CMD ["/opt/couchbase/configure.sh"] |
Basicamente, estamos apenas copiando o script para a imagem de base e executando-o. A verdadeira mágica acontece dentro do script.
Abra o configure.sh e inclua o seguinte:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
set -m /entrypoint.sh couchbase-server & dormir 15 curl -v -X POST http://127.0.0.1:8091/pools/default -d memoryQuota=512 -d indexMemoryQuota=512 curl -v http://127.0.0.1:8091/node/controller/setupServices -d services=kv%2cn1ql%2Cindex curl -v http://127.0.0.1:8091/settings/web -d port=8091 -d username=$COUCHBASE_ADMINISTRATOR_USERNAME -d password=$COUCHBASE_ADMINISTRATOR_PASSWORD curl -i -u $COUCHBASE_ADMINISTRATOR_USERNAME:$COUCHBASE_ADMINISTRATOR_PASSWORD -X POST http://127.0.0.1:8091/settings/indexes -d 'storageMode=memory_optimized' curl -v -u $COUCHBASE_ADMINISTRATOR_USERNAME:$COUCHBASE_ADMINISTRATOR_PASSWORD -X POST http://127.0.0.1:8091/pools/default/buckets -d name=$COUCHBASE_BUCKET -d bucketType=couchbase -d ramQuotaMB=128 -d authType=sasl -d saslPassword=$COUCHBASE_BUCKET_PASSWORD dormir 15 curl -v http://127.0.0.1:8093/query/service -d "statement=CREATE PRIMARY INDEX ON `$COUCHBASE_BUCKET`" fg 1 |
O Couchbase Server tem uma interface RESTful que nos permitirá concluir o assistente de configuração por meio de um script. Os comandos acima definirão as especificações e os serviços da instância, as credenciais administrativas e um Bucket.
Você notará coisas como $COUCHBASE_ADMINISTRATOR_USERNAME
dentro do script. Assim como no aplicativo Golang, estamos aproveitando as variáveis de ambiente para evitar a necessidade de codificar valores em nossa imagem. Esses valores podem ser definidos durante a implantação do contêiner.
Mais informações sobre o provisionamento de um contêiner do Couchbase Server podem ser encontradas em um Artigo anterior que escrevi sobre o assunto.
Por fim, podemos criar essa imagem personalizada do Couchbase Server. No Docker Shell, execute o seguinte:
1 |
docker build -t couchbase-custom . |
O comando acima nos deixará com um couchbase-custom
imagem.
Implantação do microsserviço em contêiner
Há algumas maneiras de implementar as imagens que criamos. Vamos explorar duas delas aqui.
Com o que sabemos sobre nossas imagens, podemos implementar nossos contêineres com os seguintes comandos:
1 2 3 4 5 6 7 8 9 |
execução do docker -d \ -p 8091-8093:8091-8093 \ -e COUCHBASE_ADMINISTRATOR_USERNAME=Administrador \ -e COUCHBASE_ADMINISTRATOR_PASSWORD=senha \ -e COUCHBASE_BUCKET=default \ -e COUCHBASE_BUCKET_PASSWORD= \ --network="docker_default" \ --name couchbase \ couchbase-custom |
O comando acima implantará o Couchbase Server enquanto mapeia as portas necessárias para o sistema operacional do host. Estamos passando cada variável de ambiente com o comando e definindo uma rede operacional. A rede é importante porque você deseja que o banco de dados e o aplicativo estejam na mesma rede de contêineres.
1 2 3 4 5 6 7 |
execução do docker -d \ -p 12345:12345 \ -e COUCHBASE_HOST=couchbase \ -e COUCHBASE_BUCKET=default \ --network="docker_default" \ --name golang projeto golang |
O comando acima implementará nossa imagem Golang enquanto mapeia as portas corretas novamente. Para o host, você notará que estamos usando couchbase
que, na verdade, é o nome do nosso outro contêiner. Os nomes dos contêineres também funcionam como nomes de host.
Na minha opinião, os comandos que você acabou de ver são uma maneira muito manual de fazer as coisas. Em vez disso, você pode criar um Docker Compose para lidar com isso para você.
Criar um docker-compose.yml em algum lugar de seu computador com o seguinte:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
versão: '2' serviços: couchbase: imagem: couchbase-custom portos: - 8091:8091 - 8092:8092 - 8093:8093 ambiente: - COUCHBASE_ADMINISTRATOR_USERNAME=Administrador - COUCHBASE_ADMINISTRATOR_PASSWORD=senha - COUCHBASE_BUCKET=padrão - COUCHBASE_BUCKET_PASSWORD= golang: imagem: golang-project portos: - 12345:12345 ambiente: - COUCHBASE_HOST=couchbase - COUCHBASE_BUCKET=padrão reiniciar: sempre |
Tudo em um arquivo Compose estará na mesma rede. Para implementar cada serviço, execute o seguinte:
1 2 |
docker-compose run -d --service-ports couchbase docker-compose run -d --service-ports golang |
Se você estiver familiarizado com o Docker Compose, estará familiarizado com docker-compose up
mas não podemos usar isso aqui. O Couchbase Server não tem um mecanismo para nos informar que está pronto, portanto, não queremos que o aplicativo Golang tente se conectar antes de estar configurado. Definitivamente, não é um grande problema quando se trata de implementação de contêineres.
Conclusão
Você acabou de ver como criar um microsserviço da Golang como um Docker que se comunica com o Couchbase Server, também executado em um contêiner do Docker. Ao transformar seu aplicativo em uma imagem do Docker, fica muito fácil distribuí-lo, pois você não precisa se preocupar com o ambiente no qual está tentando fazer a implantação. Desde que o mecanismo do Docker esteja disponível, você poderá implantar seu aplicativo. Um exemplo útil disso seria em implantação contínua de contêineres usando Jenkins.
Deseja saber como conteinerizar seu aplicativo Java? Dê uma olhada em este tutorial Eu escrevi. Também tenho um tutorial para conteinerizar um aplicativo Node.js aqui.
Deseja obter mais informações sobre como usar o Golang com o Couchbase? Dê uma olhada na seção Portal do desenvolvedor do Couchbase para obter documentação e exemplos.