Kami menggunakan arsitektur microservice secara ekstensif, meskipun kami tidak menganggapnya sebagai obat mujarab, dan sedikit lebih dari 2 tahun yang lalu kami mulai beralih ke bahasa Go. Ini relatif sederhana dan, menurut pendapat saya, sangat cocok untuk membuat layanan microser sederhana, kecil dan cepat. Kesederhanaan ini memiliki kelemahan: karena itu, ada banyak cara untuk menyelesaikan masalah yang sama.
Tampaknya, berapa banyak satu layanan microser yang pergi ke database berbeda dari layanan microser lain yang pergi ke database tetangga? Misalnya, satu tim menggunakan Go 1.9, glide, database standar / sql dan satu struktur proyek, sedangkan tim lain menggunakan Go 1.13, modul, sqlx dan, tentu saja, struktur proyek lainnya.
Ketika satu layanan mikro dalam perusahaan berbeda dari yang lain, dan pada gilirannya, berbeda dari yang ketiga, ini memperlambat pengembangan. Dan perkembangan lambat adalah kerugian untuk optimasi.
Nama saya Alexey Partilov, saya adalah pemimpin teknis tim pengembangan web di Lamoda. Pada artikel ini, saya akan menjelaskan bagaimana kita menangani variabilitas sekitar 40 dari layanan microser kami di Go. Artikel ini akan bermanfaat bagi pengembang yang baru saja bergabung dengan Go dan tidak tahu harus mulai dari mana proyek yang lebih rumit daripada helloworld.

Awalnya, Lamoda bekerja pada satu kotak monolith di PHP. Kemudian bagian dari fungsi mulai pindah ke layanan Python dan PHP baru. Sekarang kami memiliki monolit dalam PHP dengan logika bisnis yang besar dan kompleks. Aplikasi ini terlibat dalam otomatisasi kegiatan operasional kami: pengiriman, studio foto, layanan afiliasi. Kami juga memiliki monolit di Jawa, yang mengotomatiskan banyak proses kompleks di gudang.
, , .
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 . . , , .
β . , ( ), ( ).
, , , , .
, / , , .