3.9 将Golang应用部署到Docker
项目地址:https://github.com/EDDYCJY/go-gin-example
涉及知识点
Go + Docker
本文目标
将我们的 go-gin-example
应用部署到一个 Docker 里,你需要先准备好如下东西:
你需要安装好
docker
。如果上外网比较吃力,需要配好镜像源。
Docker
在这里简单介绍下Docker,建议深入学习
Docker 是一个开源的轻量级容器技术,让开发者可以打包他们的应用以及应用运行的上下文环境到一个可移植的镜像中,然后发布到任何支持Docker的系统上运行。 通过容器技术,在几乎没有性能开销的情况下,Docker 为应用提供了一个隔离运行环境
简化配置
代码流水线管理
提高开发效率
隔离应用
快速、持续部署
接下来我们正式开始对项目进行 docker
的所需处理和编写,每一个大标题为步骤大纲
Golang
一、编写Dockerfile
在 go-gin-example
项目根目录创建 Dockerfile 文件,写入内容
作用
golang:latest
镜像为基础镜像,将工作目录设置为 $GOPATH/src/go-gin-example
,并将当前上下文目录的内容复制到 $GOPATH/src/go-gin-example
中
在进行 go build
编译完毕后,将容器启动程序设置为 ./go-gin-example
,也就是我们所编译的可执行文件
注意 go-gin-example
在 docker
容器里编译,并没有在宿主机现场编译
说明
Dockerfile 文件是用于定义 Docker 镜像生成流程的配置文件,文件内容是一条条指令,每一条指令构建一层,因此每一条指令的内容,就是描述该层应当如何构建;这些指令应用于基础镜像并最终创建一个新的镜像
你可以认为用于快速创建自定义的 Docker 镜像
1、 FROM
指定基础镜像(必须有的指令,并且必须是第一条指令)
2、 WORKDIR
格式为 WORKDIR
<工作目录路径>
使用 WORKDIR
指令可以来指定工作目录(或者称为当前目录),以后各层的当前目录就被改为指定的目录,如果目录不存在,WORKDIR
会帮你建立目录
3、COPY
格式:
COPY
指令将从构建上下文目录中 <源路径> 的文件/目录复制到新的一层的镜像内的 <目标路径> 位置
4、RUN
用于执行命令行命令
格式:RUN
<命令>
5、EXPOSE
格式为 EXPOSE
<端口1> [<端口2>...]
EXPOSE
指令是声明运行时容器提供服务端口,这只是一个声明,在运行时并不会因为这个声明应用就会开启这个端口的服务
在 Dockerfile 中写入这样的声明有两个好处
帮助镜像使用者理解这个镜像服务的守护端口,以方便配置映射
运行时使用随机端口映射时,也就是
docker run -P
时,会自动随机映射EXPOSE
的端口
6、ENTRYPOINT
ENTRYPOINT
的格式和 RUN
指令格式一样,分为两种格式
exec
格式:shell
格式:
ENTRYPOINT
指令是指定容器启动程序及参数
二、构建镜像
go-gin-example
的项目根目录下执行 docker build -t gin-blog-docker .
该命令作用是创建/构建镜像,-t
指定名称为 gin-blog-docker
,.
构建内容为当前上下文目录
三、验证镜像
查看所有的镜像,确定刚刚构建的 gin-blog-docker
镜像是否存在
四、创建并运行一个新容器
执行命令 docker run -p 8000:8000 gin-blog-docker
运行成功,你以为大功告成了吗?
你想太多了,仔细看看控制台的输出了一条错误 dial tcp 127.0.0.1:3306: connect: connection refused
我们研判一下,发现是 Mysql
的问题,接下来第二项我们将解决这个问题
Mysql
一、拉取镜像
从 Docker
的公共仓库 Dockerhub
下载 MySQL
镜像(国内建议配个镜像)
二、创建并运行一个新容器
运行 Mysql
容器,并设置执行成功后返回容器ID
连接 Mysql
初始化的 Mysql
应该如图
Golang + Mysql
一、删除镜像
由于原本的镜像存在问题,我们需要删除它,此处有几种做法
删除原本有问题的镜像,重新构建一个新镜像
重新构建一个不同
name
、tag
的新镜像
删除原本的有问题的镜像,-f
是强制删除及其关联状态
若不执行 -f
,你需要执行 docker ps -a
查到所关联的容器,将其 rm
解除两者依赖关系
二、修改配置文件
将项目的配置文件 conf/app.ini
,内容修改为
三、重新构建镜像
重复先前的步骤,回到 gin-blog
的项目根目录下执行 docker build -t gin-blog-docker .
四、创建并运行一个新容器
关联
Q:我们需要将 Golang
容器和 Mysql
容器关联起来,那么我们需要怎么做呢?
A:增加命令 --link mysql:mysql
让 Golang
容器与 Mysql
容器互联;通过 --link
,可以在容器内直接使用其关联的容器别名进行访问,而不通过IP,但是--link
只能解决单机容器间的关联,在分布式多机的情况下,需要通过别的方式进行连接
运行
执行命令 docker run --link mysql:mysql -p 8000:8000 gin-blog-docker
结果
检查启动输出、接口测试、数据库内数据,均正常;我们的 Golang
容器和 Mysql
容器成功关联运行,大功告成 :)
Review
思考
虽然应用已经能够跑起来了
但如果对 Golang
和 Docker
有一定的了解,我希望你能够想到至少2个问题
为什么
gin-blog-docker
占用空间这么大?(可用docker ps -as | grep gin-blog-docker
查看)Mysql
容器直接这么使用,数据存储到哪里去了?
创建超小的Golang镜像
Q:第一个问题,为什么这么镜像体积这么大?
A:FROM golang:latest
拉取的是官方 golang
镜像,包含Golang的编译和运行环境,外加一堆GCC、build工具,相当齐全
这是有问题的,我们可以不在Golang容器中现场编译的,压根用不到那些东西,我们只需要一个能够运行可执行文件的环境即可
构建Scratch镜像
Scratch镜像,简洁、小巧,基本是个空镜像
一、修改Dockerfile
二、编译可执行文件
编译所生成的可执行文件会依赖一些库,并且是动态链接。在这里因为使用的是 scratch
镜像,它是空镜像,因此我们需要将生成的可执行文件静态链接所依赖的库
三、构建镜像
注意,假设你的Golang应用没有依赖任何的配置等文件,是可以直接把可执行文件给拷贝进去即可,其他都不必关心
这里可以有好几种解决方案
依赖文件统一管理挂载
go-bindata 一下
...
因此这里如果解决了文件依赖的问题后,就不需要把目录给 COPY
进去了
四、运行
成功运行,程序也正常接收请求
接下来我们再看看占用大小,执行 docker ps -as
命令
从结果而言,占用大小以Scratch
镜像为基础的容器完胜,完成目标
Mysql挂载数据卷
倘若不做任何干涉,在每次启动一个 Mysql
容器时,数据库都是空的。另外容器删除之后,数据就丢失了(还有各类意外情况),非常糟糕!
数据卷
数据卷 是被设计用来持久化数据的,它的生命周期独立于容器,Docker 不会在容器被删除后自动删除 数据卷,并且也不存在垃圾回收这样的机制来处理没有任何容器引用的 数据卷。如果需要在删除容器的同时移除数据卷。可以在删除容器的时候使用 docker rm -v
这个命令
数据卷 是一个可供一个或多个容器使用的特殊目录,它绕过 UFS,可以提供很多有用的特性:
数据卷 可以在容器之间共享和重用
对 数据卷 的修改会立马生效
对 数据卷 的更新,不会影响镜像
数据卷 默认会一直存在,即使容器被删除
注意:数据卷 的使用,类似于 Linux 下对目录或文件进行 mount,镜像中的被指定为挂载点的目录中的文件会隐藏掉,能显示看的是挂载的 数据卷。
如何挂载
首先创建一个目录用于存放数据卷;示例目录 /data/docker-mysql
,注意 --name
原本名称为 mysql
的容器,需要将其删除 docker rm
创建成功,检查目录 /data/docker-mysql
,下面多了不少数据库文件
验证
接下来交由你进行验证,目标是创建一些测试表和数据,然后删除当前容器,重新创建的容器,数据库数据也依然存在(当然了数据卷指向要一致)
我已验证完毕,你呢?
参考
本系列示例代码
书籍
关于
修改记录
第一版:2018年02月16日发布文章
第二版:2019年10月01日修改文章
?
如果有任何疑问或错误,欢迎在 issues 进行提问或给予修正意见,如果喜欢或对你有所帮助,欢迎 Star,对作者是一种鼓励和推进。
我的公众号
Last updated