利用docker来部署go应用程序

利用docker实现go程序的快速部署

最近很长一段时间一直经过各类渠道去了解国内外有关devops方面的实践,感觉不少的知识点都特别的分散,因此想经过系统的整理来巩固相应的知识体系。接下来会撰写一系列有关容器化的文章。
一、 利用docker部署一个简单的go程序,而且利用阿里云的平台,进行镜像的生成
二、 利用docker-compose部署一个复杂的go程序,同时部署包含多个不一样子容器的集成
三、 利用gitlab和Harbor来作ci/cdlinux

容器是独立的软件包,可以将应用程序以及全部的依赖项、工具、库、配置文件 以及运行该应用程序所须要的全部的其余内容捆绑在一块儿。
经过docker能够实现将您 的应用环境从运行的物理操做系统上面抽象一层虚拟操做环境。从而保证你的程序从开发到测试以及现场的部署,保证了环境 的一致性问题。git

建立一个简单的Go项目

在开始演习以前,咱们经过下面命令来建立一个新的目录,接下来咱们全部的文件都将存放在这个目录当中。github

mkdir go-docker
在建立完成目录以后,咱们须要用go的原生命令来初始化相应的go模块。go mod是go11新增长的功能,在这以前出现很好几种模块化的解决方案,在go11以后官方终于给出了本身的解决方案.
cd go-docker
go mod init github.com/wenchangshou2/go-docker
咱们如今建立一个简单的hello world的服务,经过下面的命令来建立一个新的文件 
touch hello_server.gogolang

如下是hello_server.go的内容docker

package main

import (
    "context"
    "fmt"
    "log"
    "net/http"
    "os"
    "os/signal"
    "syscall"
    "time"

    "github.com/gorilla/mux"
    "gopkg.in/natefinch/lumberjack.v2"
)

func handler(w http.ResponseWriter, r *http.Request) {
    query := r.URL.Query()
    name := query.Get("name")
    if name == "" {
        name = "访客"
    }
    log.Printf("接收一个新的请求 %s\n", name)
    w.Write([]byte(fmt.Sprintf("Hello, %s\n", name)))
}

func main() {
    // 建立服务和root事件
    r := mux.NewRouter()

    r.HandleFunc("/", handler)

    srv := &http.Server{
        Handler:      r,
        Addr:         ":8080",
        ReadTimeout:  10 * time.Second,
        WriteTimeout: 10 * time.Second,
    }

    // 配置日志
    LOG_FILE_LOCATION := os.Getenv("LOG_FILE_LOCATION")
    if LOG_FILE_LOCATION != "" {
        log.SetOutput(&lumberjack.Logger{
            Filename:   LOG_FILE_LOCATION,
            MaxSize:    500, // 总大小
            MaxBackups: 3,
            MaxAge:     28,   //最长保存时间
            Compress:   true, // 压缩数据,默认关闭
        })
    }

    // 启动服务
    go func() {
        log.Println("启动服务")
        if err := srv.ListenAndServe(); err != nil {
            log.Fatal(err)
        }
    }()

    // 正常关闭
    waitForShutdown(srv)
}

func waitForShutdown(srv *http.Server) {
    interruptChan := make(chan os.Signal, 1)
    signal.Notify(interruptChan, os.Interrupt, syscall.SIGINT, syscall.SIGTERM)

    // 阻塞,直到接收到信号
    <-interruptChan

    ctx, cancel := context.WithTimeout(context.Background(), time.Second*10)
    defer cancel()
    srv.Shutdown(ctx)

    log.Println("结束退出")
    os.Exit(0)
}

经过下面的命令来构建一个应用程序app

wcs@iMac  ~/demo/go-docker  go build  ✔  >4950  23:22:27
go: finding github.com/gorilla/mux v1.7.4
go: downloading github.com/gorilla/mux v1.7.4
go: extracting github.com/gorilla/mux v1.7.4
go: downloading gopkg.in/natefinch/lumberjack.v2 v2.0.0
go: extracting gopkg.in/natefinch/lumberjack.v2 v2.0.0
经过go build,会自动下载须要的包,而且生成相应的可执行程序,经过下面的命令运行
wcs@iMac  ~/demo/go-docker  ./go-docker  ✔  
2020/02/26 23:24:05 启动服务curl

如今咱们尝试经过curl 来测试功能是否可用tcp

wcs@iMac  ~  curl localhost:8080  ✔  4958 
Hello, 访客
wcs@iMac  ~  curl http://localhost:8080\?name\=Rajeev
Hello, Rajeev模块化

经过DockerFile文件来定义docker 镜像文件

如今咱们须要在上面的根目录下面新建一个文件,将文件的名称命名为Dockerfile.工具

# 构建该项目的基础镜像
FROM golang:latest

# 添加做者信息
LABEL maintainer="wcs <wenchangshou@gmail.com>"

# 设置当前的工做目录
WORKDIR /app

# 复制 go mod和sum 文件 
COPY go.mod go.sum ./

# 下载全部的依赖
RUN go mod download
# 复制当前目录源文件到工做目录下面

COPY . .
# 编译GO程序
RUN go build -o main .

# 经过EXPOSE对外暴露服务的端口号

EXPOSE 8080
# 经过下面的命令来运行可执行文件 
CMD ["./main"]

编译和运行docker镜像

上面咱们已经定义了DockerFile,如今咱们须要经过命令将DockerFile生成构建和运行相应的映像

编译镜像

docker build -t go-docker .

你懂的国内应该各类缘由,访问会特别的慢,你能够利用aliyun和daocloud来进行回事。
经过下面的命令来查看镜像是否生成成功.

docker image ls

REPOSITORY TAG IMAGE ID CREATED SIZE
go-docker lates 7654e880c09b 2 minutes ago 826MB
golang latest c3474fb0f20e 15 hours ago 809MB

运行docker镜像

如今咱们终端能够运行以前生成的镜像了

docker run -d -p 8888:8080 go-docker

3c80b9cb6b21a4fd50ea8b157ace4f3a9757e92b0e3cf9192639d30299d03d73

查看运行的容器

你能够经过下面的命令来查看正在运行的容器
docker container ls
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
3c80b9cb6b21 go-docker "./main" 45 seconds ago Up 45 seconds 0.0.0.0:8888->8080/tcp quirky_shamir

测试容器功能

如今咱们能够经过curl来测试应用是否正常

curl http://localhost:8888\?name\=wcs
Hello, wcs

中止容器

如今运行完成以后咱们就能够中止相应的容器。在每次建立容器的时候都会产生一个惟一的id,那个id用来标识相应的容器,以后能够经过该id进行相应的操做

docker container stop 3c80b9cb6b21

挂载本地目录到docker镜像中

如今咱们开始一个新的实例,在dockerfile里面提供了一个volume的字段,经过该字段能够定义容器内部的共享的目录。定义完成以后,你能够在建立容器的时候指定volume目录与本地目录的绑定。
在如下Dockerfile中,咱们声明一个volume路径/app/logs。容器会将日志文件写入/app/logs/app.log。运行docker映像时,咱们能够将宿主的目录挂载到该卷上。完成此操做后,咱们将可以从宿主的已挂载目录访问全部日志文件。

# 构建该项目的基础镜像
FROM golang:latest

# 添加做者信息
LABEL maintainer="wcs <wenchangshou@gmail.com>"

# 设置当前的工做目录
WORKDIR /app

# 编译参数
ARG LOG_DIR=/app/logs
# 建立log目录
RUN mkdir -p ${LOG_DIR}

#环境变量
ENV LOG_FILE_LOCATION=${LOG_DIR}/app.log

# 复制 go mod和sum 文件 
COPY go.mod go.sum ./

# 下载全部的依赖
RUN go mod download
# 复制当前目录源文件到工做目录下面

COPY . .
# 编译GO程序
RUN go build -o main .

# 经过EXPOSE对外暴露服务的端口号

EXPOSE 8080
# 声明Volumes
VOLUME [${LOG_DIR}]
# 经过下面的命令来运行可执行文件 
CMD ["./main"]

咱们从新编译一个新的镜像

docker build -t go-docker-voluume -f Dockerfile.volume

如今咱们建立宿主将要绑定的目录

mkdir -p ~/logs/go-docker
docker run -d -p 8889:8080 -v ~/logs/go-docker:/app/logs go-docker-volume 
394433aa0804dd24443d65090006e8becbe2aa26a883efe4324e79d4e085e261

如今咱们发现docker已经将日志写入到宿主对象的目录当中

tail -f ~/logs/go-docker/app.log

2020/02/26 16:01:38 启动服务

优化镜像

经过上面的示例,咱们生成了二个镜像,可是大家仔细查看,会发现即便这么小功能的一个程序,也会占用很大的存储空间。
docker image ls  ✔  4992  00:04:52
REPOSITORY TAG IMAGE ID CREATED SIZE
go-docker-volume latest 0b534f6bceaf 4 minutes ago 826MB
go-docker latest 7654e880c09b 19 minutes ago 826MB
golang latest c3474fb0f20e 16 hours ago 809MB
仅golang:latest镜像就占用了809MB,,而咱们生成程序也占用了826M的空间。
上面之因此这样大,是由于golang:latest里面包含了整个go的运行环境以及相应的编译环境,而咱们的应用程序仅在编译时须要这些,在编译完成以后仅运行就能够。根据这些需求咱们能够将原有的镜像拆分红多步,第一步是编译,以后咱们能够经过Alpine linux来进行最小化运行。
Dockerfile.multistage

FROM golang:latest as builder

LABEL maintainer="wcs<wenchangshou@gmail.com>"

WORKDIR /app

COPY go.mod go.sum ./

RUN go mod download

COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o main .



# 开始一个新的步骤
FROM alpine:latest

RUN apk --no-cache add ca-certificates

WORKDIR /root/
# 复制上一阶段的预构建二进制文件
COPY --from=builder /app/main .

EXPOSE 8080

CMD ["./main"]

如今咱们生成新的镜像

docker build -t go-docker-optimized -f Dockerfile.multistage .

如今咱们再看压缩后的镜像

docker image ls REPOSITORY TAG IMAGE ID CREATED SIZE go-docker-optimized latest ed8e431271dc 23 seconds ago 14.1MB go-docker-volume latest 0b534f6bceaf 11 minutes ago 826MB go-docker latest 7654e880c09b 27 minutes ago 826MB golang latest c3474fb0f20e 16 hours ago 809MB alpine latest e7d92cdc71fe 5 weeks ago 5.59MB 如今你惊讶的发现压缩后的镜像仅有14.1M

相关文章
相关标签/搜索