介绍

Go 语言可以使用内置命令行工具 go build 编译生成可执行文件。自 Go1.5 版本开始实现自举后,交叉编译也很方便,只需使用 GOOSGOARCH 环境变量指定目标平台和架构。

部署 Go 项目,只需运行可执行文件,一般有三种方式,分别是使用 nohup 命令、使用 supervisord 进程管理工具、使用 Docker。

本文我们介绍怎么使用 Docker 部署 Go 项目。

web 项目

首先,我们开发一个简单的 Go Web 项目,使用 Go 内置命令行工具 go build 编译生成可执行文件 ./hello

项目目录:

.
├── Dockerfile
├── go.mod
├── hello
├── main.go
└── service.log
 

我们使用 Go 标准库编写一个 Web 项目,运行编译生成的可执行程序,访问 http://127.0.0.1:8080/hello,输出 hello word

# 编译
go build -o hello
./hello
# 访问
curl http://127.0.0.1:8080/hello
# 输出
hello world
 

如果使用 curl 访问,可以正常输出 hello world,说明我们程序已正常运行。需要注意的是在我们构建 Docker 镜像之前,我们需要重新使用以下命令,交叉编译生成 linux 平台的可执行程序。

# 编译
GOOS=linux GOARCH=amd64 go build -o hello
 

在完成以上准备工作之后,我们开始编写 Dockerfile 文件,使该项目可以支持使用 Docker 部署。

Dockerfile 文件:

# 基础镜像
FROM alpine:3.12
# 维护者
MAINTAINER frank
# docker build 时执行命令 - 创建目录
RUN mkdir -p "/data/app" \
&& ln -sf /dev/stdout /data/app/service.log
# 工作目录
WORKDIR "/data/app"
# 拷贝
COPY hello /data/app/hello
# docker run 时执行命令
ENTRYPOINT ["./hello"]
 

在编写完 Dockerfile 文件之后,我们可以使用 docker 命令构建镜像,前提是我们本机已安装 Docker。

docker build -t hello:v1.0.0 .
 

运行以上构建 Docker 镜像的命令之后,我们就已成功构建 Docker 镜像。

 

Docker 部署

我们已经构建好了 Docker 镜像,现在可以使用 Docker 部署项目了。

首先,执行 docker images 命令,查看镜像列表。

docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
hello v1.0.0 8daaf8297956 11 seconds ago 11.7MB

运行上面命令,我们可以看到我们构建好的镜像名称为 hello 的 Docker 镜像。

然后,我们使用该镜像,运行一个容器。

docker run -d -p 9090:8080 hello:v1.0.0
74f75a374493f21caaf0f2e7f3d14698c3909446c78af6dc1172ac3f9052d839
 

需要注意的是,我们使用宿主机端口 9090 映射容器端口 8080。其中参数 -d 用于该程序在后台运行,参数 -p 用于映射端口。

查看容器列表:

docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
74f75a374493 hello:v1.0.0 "./hello" 9 seconds ago Up 8 seconds 0.0.0.0:9090->8080/tcp dazzling_ramanujan

执行 docker ps -a 命令,我们可以查看容器列表。

最后,我们使用 curl 访问 http://127.0.0.1:9090/hello,输出 hello world

# 访问
curl http://127.0.0.1:9090/hello
# 输出
hello world

如果使用 curl 访问,可以正常输出 hello world,说明我们已经成功完成使用 Docker 部署 Go 项目。

我们可以使用 docker logs 命令,查看程序日志。

docker logs -f 74
2022/04/09 05:09:08 [info]run success
2022/04/09 05:09:21 [info]run success
2022/04/09 05:09:21 [info]run success
2022/04/09 05:09:22 [info]run success

我们可以使用 docker exec 命令进入容器中,查看项目文件。

docker exec -it 74 /bin/sh
/data/app # ls -al
total 5948
drwxr-xr-x 1 root root 4096 Apr 9 05:07 .
drwxr-xr-x 1 root root 4096 Apr 9 05:07 ..
-rwxr-xr-x 1 root root 6075093 Apr 9 04:12 hello
lrwxrwxrwx 1 root root 11 Apr 9 05:07 service.log -> /dev/stdout
/data/app #

Docker 容器管理工具 docker compose

使用 Docker 部署 Go 项目,我们需要先使用 docker build 命令构建 Docker 镜像,然后使用 docker run 命令运行容器,并且命令后面一般还需要一些参数,操作多少有些繁琐。

我们可以使用 Docker 容器管理工具 docker compose 解决此问题。读者朋友们如果不了解 docker compose,请自行查阅相关资料,限于篇幅,本文不再赘述。

我们主要介绍怎么将 docker compose 和 Dockerfile 配合使用,管理容器和镜像。

我们需要创建一个 docker-compose.yml 文件,在该文件中配置运行容器需要的命令和参数。

docker-compose.yml 文件:

version: '3.8'
services:
webapp:
restart: always # Docker 重启时,容器也重启
build: # 构建 Docker 镜像
context: ./ # Dockerfile 文件的目录
dockerfile: Dockerfile # Dockerfile 文件的名称
image: hello:1.0.0 # 镜像名称和版本号
container_name: hello # 容器名称
ports: # 宿主机和容器之间映射端口
- "9090:8080"

在编写完 docker-compose.yml 文件之后,我们可以使用 docker-compose 命令行工具管理容器。

# 启动容器
docker-compose up -d
# 查看容器列表
docker-compose ps
# 查看日志
docker-compose -f
# 关闭容器
docker-compose stop
# 启动容器
docker-compose start
# 重启容器
docker-compose restart
# 关闭并删除容器
docker-compose down

需要注意的是,使用 docker-compose 命令行工具,需要在 docker-compose.yml 文件当前目录执行。



镜像大小优化Dockerfile

1)基于golang的基础镜像,在镜像中安装包依赖并生成二进制文件

FROM golang:1.20.6

ENV GOPROXY=https://goproxy.cn \
CGO_ENABLED=0 \
GOOS=linux WORKDIR /app COPY go.mod go.sum ./ # 安装依赖包
RUN go mod download COPY *.go ./ RUN go build -o /docker-gs-ping EXPOSE 8888 CMD ["/docker-gs-ping"]

然而使用这种方式打出来的包竟然有1GB+大小,岂能忍

2.使用多步骤,将打包好的可执行文件放置到一个单独的小的镜像中使用

FROM golang:1.20.6 AS build-stage

ENV GOPROXY=https://goproxy.cn \
CGO_ENABLED=0 \
GOOS=linux WORKDIR /app COPY go.mod go.sum ./
RUN go mod download COPY *.go ./ RUN go build -o /docker-gs-ping FROM build-stage AS run-test-stage
RUN go test -v ./... FROM debian:stable-slim AS build-release-stage ENV TZ=Asia/Shanghai \
DEBIAN_FRONTEND=noninteractive RUN ln -fs /usr/share/zoneinfo/${TZ} /etc/localtime \
&& echo ${TZ} > /etc/timezone \
&& dpkg-reconfigure --frontend noninteractive tzdata \
&& rm -rf /var/lib/apt/lists/* RUN useradd -U www WORKDIR /
COPY --from=build-stage /docker-gs-ping /docker-gs-ping EXPOSE 8888 USER www:www ENTRYPOINT ["/docker-gs-ping"]

打出来的镜像这次小了很多, 差不多只有100MB的大小,  那能不能再小一些呢? 使用alpine

3.将打包好的二进制文件, 直接放入基础镜像提供的alpine中运行

FROM alpine:3.12

WORKDIR /app

COPY docker-gs-ping ./

EXPOSE 8888

ENTRYPOINT ["/app/docker-gs-ping"]

直接将最终的镜像缩小到了10M左右了, ^_^

附上main文件,方便整体测试:

package main

import (
"github.com/labstack/echo/v4"
"github.com/labstack/echo/v4/middleware"
"net/http"
"os"
"time"
) func main() {
e := echo.New() e.Use(middleware.Logger())
e.Use(middleware.Recover()) e.GET("/", func(c echo.Context) error {
return c.HTML(http.StatusOK, "Hello, Docker")
}) e.GET("/health", func(c echo.Context) error {
return c.JSON(http.StatusOK, struct {
Status string
Ts time.Time
}{
Status: "OK",
Ts: time.Now(),
})
}) httpPort := os.Getenv("PORT")
if httpPort == "" {
httpPort = "8888"
}
e.Logger.Fatal(e.Start(":" + httpPort))
}

  

注意点:

1.时区一定要进行设置,默认一般都是UTC

在 Linux 系统中,控制时区和时间的主要是两个地方:

  • /etc/timezone 主要代表当前时区设置,一般链接指向/usr/share/zoneinfo目录下的具体时区。
  • /etc/localtime 主要代表当前时区设置下的本地时间。

a) 宿主机为Linux,可以使用映射的方式进行挂载

 -v /etc/timezone:/etc/timezone:ro -v /etc/localtime:/etc/localtime:ro

# 示例
docker run --name test --rm -ti -v /etc/timezone:/etc/timezone:ro -v /etc/localtime:/etc/localtime:ro alpine /bin/sh
/ # date
Fri Nov 29 16:13:55 CST 2019

b)通过环境变量

  • 适用于基于 Debian 基础镜像, CentOS 基础镜像 制作的 Docker 镜像
  • 不适用于基于 Alpine 基础镜像, Ubuntu 基础镜像 制作的 Docker 镜像

对于基于 Debian 基础镜像,CentOS 基础镜像制作的 Docker 镜像,在运行 Docker 容器时,传递环境变量-e TZ=Asia/Shanghai进去,能修改 docker 容器时区。

-e TZ=Asia/Shanghai

# 示例
docker run --name test -e TZ=Asia/Shanghai --rm -ti debian /bin/bash
/# date
Fri Nov 29 18:46:18 CST 2019

c)制作镜像时设置

Alpine)

ENV TZ Asia/Shanghai

RUN apk add tzdata && cp /usr/share/zoneinfo/${TZ} /etc/localtime \
&& echo ${TZ} > /etc/timezone \
&& apk del tzdata

# 或者将这些文件打包好直接使用
FROM alpine:3.18
#

tar cfz zoneinfo.tar.gz /usr/share/zoneinfo/

ADD zoneinfo.tar.gz /

CMD cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime


Debian)    Debian 基础镜像 中已经安装了 tzdata 包,我们可以将以下代码添加到 Dockerfile 中:

ENV TZ Asia/Shanghai

RUN apk add tzdata && cp /usr/share/zoneinfo/${TZ} /etc/localtime \
&& echo ${TZ} > /etc/timezone \
&& apk del tzdata

Ubuntu)   Ubuntu 基础镜像中没有安装了 tzdata 包,因此我们需要先安装 tzdata 包。

ENV TZ=Asia/Shanghai \
DEBIAN_FRONTEND=noninteractive RUN apt update \
&& apt install -y tzdata \
&& ln -fs /usr/share/zoneinfo/${TZ} /etc/localtime \
&& echo ${TZ} > /etc/timezone \
&& dpkg-reconfigure --frontend noninteractive tzdata \
&& rm -rf /var/lib/apt/lists/*

Centos)  CentOS 基础镜像 中已经安装了 tzdata 包,我们可以将以下代码添加到 Dockerfile 中。

ENV TZ Asia/Shanghai

RUN ln -fs /usr/share/zoneinfo/${TZ} /etc/localtime \
&& echo ${TZ} > /etc/timezone

常见问题:

1.有人可能会使用scratch作为go二进制运行的基础镜像, 但是会出现时区问题

如果你是从0自己制作一个镜像的话,可以使用scratch作为基础镜像,如果是部署项目的话使用alpine就足够了

golang之项目部署的更多相关文章

  1. Golang优秀开源项目汇总, 10大流行Go语言开源项目, golang 开源项目全集(golang/go/wiki/Projects), GitHub上优秀的Go开源项目

    Golang优秀开源项目汇总(持续更新...)我把这个汇总放在github上了, 后面更新也会在github上更新. https://github.com/hackstoic/golang-open- ...

  2. gin项目部署到服务器并后台启动

    前言 我们写好的gin项目想要部署在服务器上,我们应该怎么做呢,接下来我会详细的讲解一下部署教程. 1.首先我们要有一台虚拟机,虚拟机上安装好go框架. 2.将写好的项目上传到虚拟机上. 3.下载好项 ...

  3. jsp项目部署

    每新建一个项目都要发布到服务器,也就是项目部署,在tomcat中的  tomcat\Tomcat 6.0\webapps  路径下就会新建你的项目文件夹 webapps是tomcat的默认访问路径,很 ...

  4. javaWeb项目部署到阿里云服务器步骤

    记录web项目部署到阿里云服务器步骤 (使用 web项目.阿里云服务器.Xftp.Xshell),敬请参考和指正 1.将要部署的项目打包成WAR文件格式,可以在MyEclipse.Eclipse都可以 ...

  5. Spring+SpringMvc+Mybatis框架集成搭建教程四(项目部署及测试)

    在IDEA中将项目部署到本地Tomcat下进行运行并验证整合结果 (1).点击如下图所示的下拉按钮,弹出Edit Configurations...后点击该项. (2).跳出如下界面后,点击红框内的& ...

  6. SSIS2012 项目部署模型

    SSIS 2012 支持两种部署模型:项目部署模型和包部署模型. 使用项目部署模型可以将项目部署到 Integration Services 服务器,使用包部署模型可以将单独的包部署到Integrat ...

  7. 项目部署到tomcat Root中后导致 WebApplicationContext 初始化两次的解决方法

    上一篇文章刚说项目部署到tomcat的ROOT中,今天就发现一个问题.通过eclipse启动tomcat时候,WebApplicationContext 初始化两次: 现象:   通过eclipse控 ...

  8. 百度BAE JAVA环境项目部署和调试

    起初在一个应用挂在虚拟主机上,昨天早上虚拟主机挂了.本来考虑迁移到SAE上的,但之前发现SAE的JVM云豆消耗的太快(PS:我是中级开发者,每月 10000云豆,如果有哪位大神对SAE JAVA云豆能 ...

  9. 如何正确的将J2ee项目部署到Tomcat

    如何正确的将J2ee项目部署到Tomcat 1.打开配置文件(我的如下:C:\Program Files\Apache Software Foundation\Tomcat 7.0\conf\serv ...

  10. SSIS 项目部署模型

    微软 BI 系列随笔 - SSIS 2012 基础 - SSIS 项目部署模型 关于部署 SSIS 2012 支持两种部署模型:项目部署模型和包部署模型. 使用项目部署模型可以将项目部署到 Integ ...

随机推荐

  1. 深入理解Argo CD工作原理

    1. ArgoCD 的架构 ArgoCD 是一个 Kubernetes 原生的持续交付工具,它通过监控 Git 仓库中的应用定义来自动部署应用到 Kubernetes 集群.其核心架构由以下几个关键组 ...

  2. 006.MinIO基础使用

    图形界面基础使用 bucket bucket创建 图形界面创建bucket. 特性: Versioning 开启版本控制,开启版本控制则允许在同一键下保持同一对象的多个版本. Object Locki ...

  3. Angular 16+ 高级教程 – 谈谈 ASP.NET Core & Angular & React 在业务开发上各自的优势和体验

    前言 日常, 我的开发都围绕着 ASP.NET Core 和 Angular. 这篇想聊聊它们各自的特点和解决问题的方式. 以及最重要的, 我们该在什么时候采用何种方案更为妥当. 浅谈项目分类 我一般 ...

  4. CSS – Dimension min-content, max-content, fit-content

    前言 无意间在 practice 的时候看到视频使用, 以前没有听过. 它有点像 Figma 的 hug content, 据说 CSS 2.1 也是有类似的概念, 只是没有被正式纳入 CSS 里. ...

  5. C++ STL map/multimap容器

    map/multimap容器 Map的特性是,所有元素都会根据元素的键值自动排序.Map所有的元素都是pair,同时拥有实值和键值,pair的第一个元素被视为键值,第二个元素被视为实值,map不允许两 ...

  6. C# 裁剪PDF页面

    在处理PDF文档时,有时需要精确地裁剪页面以适应特定需求,比如去除广告.背景信息或者仅仅是为了简化文档内容.本文将介绍如何使用免费.NET控件通过C#实现裁剪PDF页面. 免费库 Free Spire ...

  7. 【赵渝强老师】Oracle RAC集群的概念

    一.什么是Oracle RAC(Real Application Cluster)? Oracle RAC 是一个具有共享缓存架构的集群数据库,它克服了传统的无共享方法和共享磁盘方法的限制,为您的所有 ...

  8. go中能比较和不能比较的数据类型

    在 Go 语言中,比较操作符(== 和 !=)可以用于许多数据类型,但也有一些数据类型不支持直接比较.下面详细解释哪些数据类型可以比较,哪些不能比较,以及相关的规则和原因. 可以比较的数据类型 布尔型 ...

  9. js中,什么是数组 , 数组有几种创建方式?

    1. 什么是数组? 数组是可以把一组相关的数据一起存放,并提供方便的访问(存取) 数组是指一组数据的集合,其中每个数据被称作元素(数组单元),数组单元可以是任意类型的数据,数组是一种将一组数据存储在单 ...

  10. ABC270-d

    题目 首先贪心是行不通的,考试的时候打了贪心,挂了...... 举个反例: 10 2 3 4 贪心枚举答案为4,但若高桥先选3,最大值为6. 其实考试的时候想到了dp,但是不会打 悲 因为青木也是聪明 ...