• Introduction to the
  • Build in Docker
  • Integrated in Docker-compose
  • Use the nginx reverse proxy
    • Modify the API
    • Create nginx service
    • Updating the database
    • Start the
  • conclusion
  • Code for the current section

Introduction to the

At the end of the day, the testing and documentation are done, and deployment is all that’s left.

Go Run can be used directly during normal testing, but when it comes to deployment, for compiled languages, go Build will definitely be used to generate binaries.

Build in Docker

Because the whole system is based on docker-compose, you need to write a Dockerfile to build the whole project into a mirror in Docker.

This way, you can run it directly in docker. The process of generating binaries for each local build is transformed into rebuilding the Docker image.

Dockerfile is as follows:

FROM golang:1.13 as build

ENV GOPROXY="https://goproxy.io"
# https://stackoverflow.com/questions/36279253/go-compiled-binary-wont-run-in-an-alpine-docker-container-on-ubuntu-host
# build for static link
ENV CGO_ENABLED=0
WORKDIR /app
COPY . /app
RUN make build

# production stage
FROM alpine as production

WORKDIR /app
COPY ./conf/ /app/conf
COPY --from=build /app/web /app
EXPOSE 8081
ENTRYPOINT ["/app/web"]
CMD [ "-c"."./conf/config_docker.yaml" ]
Copy the code

A two-phase build was used, first building the binaries in the normal Golang image and then copying them to the Alpine image to reduce the size of the image after the build.

The environment variable CGO_ENABLED=0 should be set during build time to disable CGO dynamic linking. For details, see StackOverflow.

Integrated in Docker-compose

When the Dockerfile is finished, you can directly build the image and run a test to see if it works.

docker build -t go_web .
docker run -p 8081:8081 go_web
Copy the code

Once all is well, you can integrate it into docker-comemage.yaml and name it a service.

app:
  build:
    context: .
  depends_on:
    - mysql
Copy the code

There is a dependency, after all, mysql has to start first. The reason why the ports are not exposed is because you are using an Nginx reverse proxy.

Use the nginx reverse proxy

Docker-compose can manually scale up to several instances of a SERVICE.

Usage: up [options] [--scale SERVICE=NUM...]  [SERVICE...]Copy the code

Although the Docker-compose scale is no longer popular after Kubernetes came out, it is still implemented. Here, we only focus on the expansion of app, i.e. the current project, and ignore other dependencies, such as database, etc.

Modify the API

First, modify the /check/ Health API to return hostname so you can see the effect.

var hostname string

func init(a) {
	name, err := os.Hostname()
	iferr ! =nil {
		name = "unknow"
	}
	hostname = name
}

// HealthCheck returns a heartbeat response
func HealthCheck(ctx *gin.Context) {
	message := fmt.Sprintf("OK from %s", hostname)
	ctx.String(http.StatusOK, message)
}
Copy the code

After the changes are complete, rebuild the image so that the changes can take effect.

Create nginx service

Configure nginx in Docker-compose.

nginx:
  image: nginx:stable-alpine
  ports:
    - 80: 80
  depends_on:
    - app
  volumes:
    - ./conf/nginx_web.conf:/etc/nginx/conf.d/default.conf
  command: nginx -g 'daemon off; '
Copy the code

Then write nginx configuration files:

upstream web {
  server app:8081;
}

server {
  listen 80;
  server_name localhost;

  location / {
    # https://stackoverflow.com/questions/42720618/docker-nginx-stopped-emerg-11-host-not-found-in-upstream
    resolver 127.0.0.1;

    proxy_set_header Host $http_host;
    proxy_set_header X-Forwarded-Host $http_host;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Real-IP $remote_addr;

    client_max_body_size 5m;

    proxy_passhttp://web; }}Copy the code

There’s a reverse proxy set up to redirect all requests to app:8081, the port exposed by the application server,

Note that resolver 127.0.0.1 is set; Otherwise, nginx will crash when app:8081 cannot be connected in the first place.

So why not start Nginx when you’re ready? This is because the depends_on in docker-compose only guarantees the start order and does not confirm if it is ready.

Updating the database

The same is true for databases. We need to set up a retry mechanism to ensure that the database has been started.

func openDB(username, password, addr, name string) *gorm.DB {
	config := fmt.Sprintf(
		"%s:%s@tcp(%s)/%s? charset=utf8mb4&parseTime=%t&loc=%s&timeout=10s",
		username,
		password,
		addr,
		name,
		true.// "Asia%2FShanghai", // must be of url.queryescape
		"Local".)var db *gorm.DB
	var err error
	for i := 0; i < 10; i++ {
		db, err = gorm.Open("mysql", config)
		if err == nil {
			break
		}
		time.Sleep(time.Second * 3)}if db == nil {
		logrus.Fatalf("Database connection failed. Database name: %s. Error message: %s", name, err)
	}
	logrus.Infof("Database connection successful, database name: %s", name)

	setupDB(db)
	return db
}
Copy the code

Also, set up an initialization script when the database is started so that you don’t have to create the database manually.

mysql:
  image: mysql:8
  command: --default-authentication-plugin=mysql_native_password --init-file /data/application/init.sql
  environment:
    MYSQL_ROOT_PASSWORD: "1234"
  ports:
    - 3306: 3306
  volumes:
    - ./script/db.sql:/data/application/init.sql
Copy the code

The database initialization script is simple and simply checks to see if a particular database exists and creates it if it does not.

CREATE DATABASE IF NOT EXISTS `db_apiserver`;
Copy the code

Start the

When all the changes are complete, you can start and try.

docker-compose up --scale app=3 nginx
Copy the code

This launches three instances of apps.

If you continue to visit http://127.0.0.1:80/v1/check/health, you should get three results, similar to the following:

OK from 5f8a835b6797
OK from b6dbb50cecd5
OK from 87e98121950d
Copy the code

The first few accesses may return error 502 because the app is still connecting to the database and has not been started.

You can then modify the configuration of Nginx to experience the various load balancing mechanisms built into Nginx. It is recommended to use this in conjunction with the previous use of Go to add Nginx proxies.

conclusion

There are many Go deployments, just choose the right one. You can also cross-compile binaries for each platform if required.

Code for the current section

As version V0.17.0