Docker 快速部署 Golang 1.12 开发环境 代码热更新

  • 原创
  • Madman
  • /
  • /
  • 3
  • 11493 次阅读

docker.png

Synopsis: 如果你不想看如何使用 Docker 和 reflex 实现 Golang 源代码热更新并自动重新编译、运行的原理,可以直接跳到本文的章节 2 如何快速使用

1. 原理解析

1.1 构建包含 reflex 的镜像

你需要先安装 Docker-CE,可参考: http://www.madmalls.com/blog/post/visualizing-docker-containers-and-images/#1-docker

然后,克隆我放在 Github 上的项目:

[root@CentOS ~]# git clone https://github.com/wangy8961/go-docker-dev.git
[root@CentOS ~]# cd go-docker-dev/

如何实现 Golang 代码热更新?

我们会使用 reflex 来监测以 .go 结尾的源代码或 go.mod(用于包管理器 Go Modules)的变化,一旦这些文件内容改变了,reflex 就会调用我们给它指定的配置文件 reflex.conf 中的指令 sh -c /usr/src/build.sh(即执行此脚本)

-r '(\.go$|go\.mod)' -s -- sh -c /usr/src/build.sh

build.sh 脚本的作用就是切换到 容器内 Golang 源代码所在目录(比如 /app),然后进行编译并运行:

#!/bin/bash

cd /app
# 编译,GO_BUILD_FLAGS 是可选的环境变量值
go build -o app ${GO_BUILD_FLAGS}
# 运行,APP_RUN_FLAGS 是可选的环境变量值
./app ${APP_RUN_FLAGS}

现在,我们创建将会运行 reflex -c /usr/src/reflex.conf 的 Docker 基础镜像的 Dockerfile

FROM golang:1.12.5-stretch

# 设置代理,因为后续 go get github.com/cespare/reflex 会下载依赖 golang.org/x/sys/unix 等
ENV HTTP_PROXY='socks5://127.0.0.1:1080'
ENV HTTPS_PROXY='socks5://127.0.0.1:1080'
# 安装 reflex 包
RUN go get github.com/cespare/reflex
# 复制 reflex 的配置文件到容器中的根目录下
COPY reflex.conf /usr/src
COPY build.sh /usr/src
# 设置容器内的工作目录,用于 RUN, CMD, ENTRYPOINT, COPY, ADD 等指令
WORKDIR /app
# 容器启动时,运行 reflex -c /usr/src/reflex.conf
# 由于工作目录已切换到 /app,所以 reflex 会监控 /app 目录下的 .go 或 go.mod 文件的变化
ENTRYPOINT ["reflex", "-c", "/usr/src/reflex.conf"]

注意: 后续构建此镜像时下载 Go 依赖包,需要科学上网,可参考: http://www.madmalls.com/blog/post/build-shadowsocks-server-on-centos/#33-linux-client 。我的 Docker 宿主机安装了 Shadowsocks 客户端,并使用 1080 端口,然后我们可以构建此镜像了,需要指定 --network host 更改网络模式(默认是 bridge 模式):

[root@CentOS go-docker-dev]# docker build --network host -t go-docker-dev:latest .
Sending build context to Docker daemon  75.26kB
Step 1/8 : FROM golang:1.12.5-stretch
 ---> 7ced090ee82e
Step 2/8 : ENV HTTP_PROXY='socks5://127.0.0.1:1080'
 ---> Running in 253791251a63
Removing intermediate container 253791251a63
 ---> c786afb6d6db
Step 3/8 : ENV HTTPS_PROXY='socks5://127.0.0.1:1080'
 ---> Running in cfca59a05f33
Removing intermediate container cfca59a05f33
 ---> 6aef3386ba42
Step 4/8 : RUN go get github.com/cespare/reflex
 ---> Running in fe62a278b71c
Removing intermediate container fe62a278b71c
 ---> 0719584291f8
Step 5/8 : COPY reflex.conf /usr/src
 ---> ab3a4f0441e9
Step 6/8 : COPY build.sh /usr/src
 ---> 9fd7880705d3
Step 7/8 : WORKDIR /app
 ---> Running in 505b279617b5
Removing intermediate container 505b279617b5
 ---> 5e36e28a80b5
Step 8/8 : ENTRYPOINT ["reflex", "-c", "/usr/src/reflex.conf"]
 ---> Running in dab2661248e8
Removing intermediate container dab2661248e8
 ---> 4b966b116294
Successfully built 4b966b116294
Successfully tagged go-docker-dev:latest

1.2 新建 Golang 项目

建议你将 Golang 升级到 1.12,可以支持 Go Modules 进行包管理,非常简单好用。假设你现在要开发一个新项目,名称为 go-example

[root@CentOS go-docker-dev]# mkdir go-example
[root@CentOS go-docker-dev]# cd go-example/
[root@CentOS go-example]# go mod init go-example
go: creating new go.mod: module go-example
[root@CentOS go-example]# cat go.mod 
module go-example

go 1.12

现在,用你的 IDE 编译器打开此项目,比如我使用 VSCode。我们用官方模块 net/http 包来搭建一个简单的 HTTP 服务器,新建 go-docker-dev/go-example/main.go 文件:

package main

import (
    "flag"
    "fmt"
    "log"
    "net/http"
)

func main() {
    port := flag.Int("port", 50051, "the port to serve on")
    flag.Parse()

    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintf(w, "Hello, you've requested: %s\n", r.URL.Path)
    })

    err := http.ListenAndServe(fmt.Sprintf(":%d", *port), nil)
    if err != nil {
        log.Fatalf("failed to listen: %v", err)
    }
    fmt.Printf("server listening on port: %v\n", fmt.Sprintf(":%d", *port))
}

注意,可以通过命令行参数为 HTTP 服务器指定要监听的端口(默认为 50051),比如 -port 80

启动容器:

[root@CentOS go-docker-dev]# docker run -d \
  --name go-example \
  -e "APP_RUN_FLAGS=-port 80" \
  -p 80:80 \
  --rm \
  -v $PWD/go-example:/app \
  go-docker-dev:latest

b78cfaf9eac6147ca9cf6c84eb2355057a1d1323a086b2a3fd987495e44edc63

[root@CentOS go-docker-dev]# docker ps
CONTAINER ID        IMAGE                  COMMAND                  CREATED             STATUS              PORTS                NAMES
b78cfaf9eac6        go-docker-dev:latest   "reflex -c /usr/src/…"   4 seconds ago       Up 3 seconds        0.0.0.0:80->80/tcp   go-example

你可以进去容器内部看看它启动了哪些进程:

[root@CentOS go-docker-dev]# docker exec -it go-example /bin/bash
root@b78cfaf9eac6:/app# ps -ef
UID        PID  PPID  C STIME TTY          TIME CMD
root         1     0  0 06:48 ?        00:00:00 reflex -c /usr/src/reflex.conf
root        17     1  0 06:48 pts/0    00:00:00 sh -c /usr/src/build.sh
root        18    17  0 06:48 pts/0    00:00:00 /bin/bash /usr/src/build.sh
root       125    18  0 06:48 pts/0    00:00:00 ./app -port 80  # 我们通过环境变量和 build.sh 来指定命令行参数
root       131     0  4 06:51 pts/1    00:00:00 /bin/bash
root       138   131  0 06:51 pts/1    00:00:00 ps -ef
root@b78cfaf9eac6:/app# exit
exit

我的宿主机 IP 为 192.168.40.128,我们通过浏览器访问看看:

reflex 01

现在,要实现热更新了哦,修改 main.go 的内容,添加:

func main() {
    ...
    fs := http.FileServer(http.Dir("static/"))
    http.Handle("/static/", http.StripPrefix("/static/", fs))
    ...
}

一旦你保存文件,容器内的 reflex 就会帮你自动重新编译源代码,并重新启动应用。现在,我们创建新目录 go-docker-dev/go-example/static/,并复制一张图片 madmalls.jpg 到此目录下

然后,你再通过浏览器访问:

reflex 02

2. 如何快速使用

你只需要先安装 DokcerDocker Compose,并克隆我放在 Github 上的项目:

[root@CentOS ~]# git clone https://github.com/wangy8961/go-docker-dev.git
[root@CentOS ~]# cd go-docker-dev/

然后修改 docker-compose.yml 文件中内容即可,比如修改 ports 改变容器内映射到宿主机的端口号,修改 volumes 将宿主机上你的项目源代码目录映射到容器内的 /app 目录下(注意: 只能映射到 /app 目录,因为 build.sh 脚本只会到此目录来编译和运行应用),比如:

version: "3.7"
services:

  go-example:
    # building image from ./Dockerfile, and change network mode to 'host', because 'go get' need proxy in Dockerfile
    build:
      context: .
      network: host
    # specify the image name after builded
    image: go-docker-dev:latest
    ports:
      - "80:80"
    # go-example is your Golang project name,
    # don't change the target path '/app', because 'reflex' will go to there and then run 'go build .'
    volumes:
      - "$PWD/go-example:/app"
    # specify container name
    container_name: go-example
    # environment file
    env_file:
      - .env

此文件说明你的 Golang 源代码目录为 go-docker-dev/go-example

如果你想指定编译选项或应用运行时的命令行参数,都可以修改 .env 文件的内容:

GO_BUILD_FLAGS=-race
APP_RUN_FLAGS=-port 80

然后通过 Docker Compose 启动 Golang 开发环境的容器即可(注意: 假设你可以使用 127.0.0.1:1080 代理):

[root@CentOS go-docker-dev]# docker-compose up
Building go-example
Step 1/8 : FROM golang:1.12.5-stretch
 ---> 7ced090ee82e
Step 2/8 : ENV HTTP_PROXY='socks5://127.0.0.1:1080'
 ---> Running in c90a5c67b8a3
Removing intermediate container c90a5c67b8a3
 ---> b4a255b862a0
Step 3/8 : ENV HTTPS_PROXY='socks5://127.0.0.1:1080'
 ---> Running in e186621097a4
Removing intermediate container e186621097a4
 ---> c52fcc75f69c
Step 4/8 : RUN go get github.com/cespare/reflex
 ---> Running in 413f495624b7
Removing intermediate container 413f495624b7
 ---> ce99643777f7
Step 5/8 : COPY reflex.conf /usr/src
 ---> ba5af0210f69
Step 6/8 : COPY build.sh /usr/src
 ---> a13e467a9ffd
Step 7/8 : WORKDIR /app
 ---> Running in f372bcfd30f3
Removing intermediate container f372bcfd30f3
 ---> 6b50f6a9d219
Step 8/8 : ENTRYPOINT ["reflex", "-c", "/usr/src/reflex.conf"]
 ---> Running in ae580ee69746
Removing intermediate container ae580ee69746
 ---> 33efe4b5ac0f

Successfully built 33efe4b5ac0f
Successfully tagged go-docker-dev:latest
WARNING: Image for service go-example was built because it did not already exist. To rebuild this image you must use `docker-compose build` or `docker-compose up --bui
ld`.Creating go-example ... done
Attaching to go-example
go-example    | [00] Starting service

docker-compose up -d 在后台运行各容器,Docker 的详细使用可参考: http://www.madmalls.com/blog/post/visualizing-docker-containers-and-images/

未经允许不得转载: LIFE & SHARE - 王颜公子 » Docker 快速部署 Golang 1.12 开发环境 代码热更新

分享

作者

作者头像

Madman

如需 Linux / Python 相关问题付费解答,请按如下方式联系我

3 条评论

kenkk
kenkk

大佬 全部的课程我想看 该怎么个流程 不想一个一个付费

Madman
Madman kenkk Author
millian
millian

这里输入代码

专题系列

文章目录