Fazemos amplo uso da arquitetura de microsserviço, embora não a consideremos uma panacéia, e há pouco mais de dois anos começamos a mudar para o idioma Go. É relativamente simples e, na minha opinião, muito adequado para criar microsserviços simples, pequenos e rápidos. Essa simplicidade tem uma desvantagem: por causa disso, existem muitas maneiras de resolver o mesmo problema.
Parece, quanto um microsserviço que vai para o banco de dados difere de outro microsserviço que vai para o banco de dados vizinho? Por exemplo, uma equipe usa Go 1.9, glide, o banco de dados padrão / sql e uma estrutura de projeto, enquanto ao mesmo tempo outra equipe usa Go 1.13, módulos, sqlx e, é claro, outra estrutura de projeto.
Quando um microsserviço em uma empresa é diferente de outro e, por sua vez, é diferente de um terço, isso atrasa o desenvolvimento. E desenvolvimento lento é uma perda para otimização.
Meu nome é Alexey Partilov, sou líder técnico de uma equipe de desenvolvimento web na Lamoda. Neste artigo, descreverei como lidamos com a variabilidade de cerca de 40 dos nossos microsserviços on Go. Este artigo será útil para desenvolvedores que estão ingressando no Go e não sabem por onde começar um projeto mais complexo que o helloworld.

Inicialmente, Lamoda trabalhou em um único monólito em caixa em PHP. Em seguida, parte das funções começou a passar para os novos serviços Python e PHP. Agora, temos monólitos em PHP com uma lógica de negócios grande e complexa. Esses aplicativos estão envolvidos na automação de nossas atividades operacionais: entrega, estúdio fotográfico, serviço afiliado. Também temos um monólito em Java, que automatiza muitos processos complexos no armazém.
, , .
e-commerce Python Golang. , UI Python+Django.
, . .
, . backend- , , , , .
.
0. Spec first
, : , OpenAPI.
:
- Backend-, , - . .
- Frontend- backend, . mock backend .
- boilerplate- c , . , , , .
OpenAPI- go-swagger ( gogi). opensource, , .
, . , , , .
, docker-. , . docker. Makefile:
.PHONY: generate
generate:
#
rm -rf internal/generated/*
docker run -it \
#
-v $(PWD)/internal/generated:$(DOCKER_SOURCE_DIR)/internal/generated \
# OpenAPI
-v $(PWD)/specs:$(DOCKER_SOURCE_DIR)/specs \
# gogi
-v $(PWD)/gogi.yaml:$(DOCKER_SOURCE_DIR)/gogi.yaml \
# workdir
-w="$(DOCKER_SOURCE_DIR)" \
gotools.docker.lamoda.ru/gogi:v1.3.2 generate
1.
. , , . , -. — , :
/
├── cmd
├── deployments
├── internal
├── migrations
├── specs
├── tests
├── go.mod
├── go.sum
├── Dockerfile
├── main.go
...
.
, ( Postgres), production-, Kafka API. ? . .
cookiecutter. . . , .
2. Go modules
1.11 Go go modules. 1.14 production-ready. glide, go modules, , , Go-. Go modules $GOPATH/pkg/mod GOPATH.
Go (build, get, test). , build . .
golang , , . , .
1. go modules. Go 1.13, $GOPATH, go mod
go env -w GOMODULE111="on"
2. RSA- . RSA- c , , ssh-agent. .
ssh-add < RSA >
3. Go proxy. Go 1.13, - proxy.golang.org, . , .
go env -w GOPRIVATE=< >
4. go.mod-. go.mod . (, glide), mod- lock-. go.mod — , .
go mod init < >
, , , github.com/spf13/cobra.
5. . . , , , . , .
go test ./...
3. Docker-
Lamoda Kubernetes-, Docker- . , Go- Docker- golang. c Docker-. Docker- , .
ARG version
FROM golang:$version
ENV GOOS linux
ENV GOARCH amd64
ENV CGO_ENABLED 0
ENV GO111MODULE on
#
RUN go get github.com/axw/gocov/gocov && \
go get github.com/AlekSi/gocov-xml@d2f6da892a0d5e0b587526abf51349ad654ade51 && \
go get golang.org/x/tools/cmd/goimports && \
curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | sh -s v1.23.7 && \
go get -u github.com/jstemmer/go-junit-report@af01ea7f8024089b458d804d5cdf190f962a9a0c && \
rm -rf /go/pkg/mod/
#
COPY ./ssh/id_rsa /root/.ssh/id_rsa
COPY ./ssh/id_rsa.pub /root/.ssh/id_rsa.pub
# Copy Lamoda Root certificate needed to go to the corporate sites without
# SSL warning
COPY ./certs/LamodaCA.crt /usr/local/share/ca-certificates/LamodaCA.crt
RUN echo "StrictHostKeyChecking no" > /root/.ssh/config && \
chmod 600 /root/.ssh/id_rsa && \
chmod 600 /root/.ssh/id_rsa.pub && \
chmod 755 /root/.ssh && \
update-ca-certificates && \
# insteadOf. Bitbucket .
git config --global url.ssh://git@stash.lamoda.ru:7999.insteadOf https://stash.lamoda.ru/scm
CMD ["/bin/sh"]
golang- . golang production-. , ( ) : debian alpine, go, . Docker- , Docker multistage.
, Docker multistage — Docker, , , , ..
:
FROM gotools.docker.lamoda.ru/base-mod:1.14.0 as build
ENV GOOS linux
ENV GOARCH amd64
ENV CGO_ENABLED 0
ENV GO111MODULE on
WORKDIR /go/src/stash.lamoda.ru/ecom/discounts.endpoint
#
COPY go.mod .
COPY go.sum .
# go-. - Docker go.mod go.sum.
RUN go mod download
COPY . .
RUN make build
# docker ca-certificates.crt
FROM scratch
COPY --from=build /go/src/stash.lamoda.ru/ecom/discounts.endpoint/discounts /bin/discounts
COPY --from=build /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt
ENTRYPOINT ["/bin/discounts"]
4.
, , Postgres. , . , DBA, . . , . SQL- DBA DevOps production .
- Jira, migrate. , . , , migrate Docker- — .
— migrations <№ >_<>.[up|down].sql
migrations
├── 00001_init.down.sql
├── 00001_init.up.sql
├── 00002_create_ui_users_table.down.sql
├── 00002_create_ui_users_table.up.sql
...
up — , .. ,
down — , .. .
SQL-, .
«»:
BEGIN;
CREATE SEQUENCE ui_users_id_seq INCREMENT BY 1 MINVALUE 1 START 1;
CREATE TABLE ui_users (
id INT NOT NULL,
username VARCHAR(180) NOT NULL,
username_canonical VARCHAR(180) NOT NULL,
email VARCHAR(180) NOT NULL,
email_canonical VARCHAR(180) NOT NULL,
enabled BOOLEAN NOT NULL,
salt VARCHAR(255),
password VARCHAR(255) NOT NULL,
last_login TIMESTAMP(0) WITHOUT TIME ZONE DEFAULT NULL,
confirmation_token VARCHAR(180) DEFAULT NULL,
password_requested_at TIMESTAMP(0) WITHOUT TIME ZONE DEFAULT NULL,
roles TEXT NOT NULL,
PRIMARY KEY(id)
);
COMMIT;
«»:
BEGIN;
DROP SEQUENCE public.ui_users_id_seq;
DROP TABLE ui_users;
COMMIT;
, :
migrate -database ${DB_DSN} -path db/migrations up
:
migrate create -ext sql -dir migrations -seq create_table_foo
, , . : . , , . migrate .
5. development-
Lamoda , , . / , . , Postgres . Postgres . / development , Docker Compose.
Compose development . , :
- Postgres,
- migrate, Postgres,
- dev- .
. , . , , .
Docker-compose :
version: "3.7"
services:
# dev-
gift-certificates-dev:
container_name: gift-certificates-dev
# , pull docker-
build:
context: ../
# Dockerfile multistage,
# . ,
#
# go run.
target: build
# environment .
env_file:
- local.env
#
depends_on:
- gift-certificates-db
# .
# go run
volumes:
- "..:/app/"
working_dir: "/app"
#
command: "go run main.go"
depends_on:
- gift-certificates-db
# Postgres
gift-certificates-db:
container_name: gift-certificates-db
image: postgres:11.4
#
# env_file, default.env
# ,
environment:
- POSTGRES_DB=gift_certificates
- POSTGRES_USER=gift_certificates
- POSTGRES_PASSWORD=gift_certificates
- POSTGRES_PORT=5432
# docker
# , , docker
ports:
- 6543:5432
#
gift-certificates-migrate:
container_name: gift-certificates-migrate
image: "migrate/migrate:v4.4.0"
depends_on:
- gift-certificates-db
volumes:
- "../migrations:/migrations"
command: ["-path", "/migrations/", "-database", "$SERVICE_DB_DSN", "up"]
Docker-compose Makefile :
#
#
.PHONY: test
test: dev-migrate
go test -cover -coverprofile=coverage.out ./...
go tool cover -html=coverage.out -o coverage.html
# dev
.PHONY: dev-server
dev-server:
docker-compose -f deployments/docker-compose.yaml up -d gift-certificates-dev
#
.PHONY: dev-migrate
dev-migrate:
docker-compose -f deployments/docker-compose.yaml run --rm --service-ports gift-certificates-migrate
# dev
.PHONY: dev-down
dev-down:
docker-compose -f deployments/docker-compose.yaml down
6.
Go gofmt, , : goimports, go-critic, gocyclo .. , IDE CI . golangci-lint, Go . , -.
, «». , , «» , .
:
curl -sfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh| sh -s -- -b $(go env GOPATH)/bin v1.23.7
:
run:
tests: false
#
skip-dirs:
- generated
skip-files:
- ".*easyjson\\.go$"
output:
print-issued-lines: false
issues:
#
new-from-rev: 7cdb5ce7d7ebb62a256cebf5460356c296dceb3d
exclude-rules:
- path: internal/common/writer.go
linters:
- structcheck
#
text: "`size` is unused"
:
golangci-lint run ./...
golangci-lint pre-commit, .
7. Pre-commit hook’
Pull Request : , / , . bash- git pre-commit . , -. : .
python pre-commit:
pip install pre-commit
, .
repos:
- repo: https://github.com/golangci/golangci-lint
rev: v1.23.7
hooks:
- id: golangci-lint
pre-commit install
golangci-lint . hook' . all-files , staging'e:
pre-commit run --all-files
hook' test runner'. https://pre-commit.com
8.
Gonkey — , Lamoda. , . , Gonkey, habr.ru “Gonkey — ”.
Gonkey :
- HTTP- , ,
- , ( YAML-),
- ( , Gonkey ),
- Allure-.
GitHub.
9.
, «» . : , , , .
, Confluence . , « ». cookiecutter.
, cookiecutter — python-, jinja- . jinja- .
:
cookiecutter https://stash.lamoda.ru/gotools/cookiecutter-go
, , , .. . cookiecutter , .
Lamoda . . , , .
— . , ( ), ( ).
, , , , .
, / , , .