Docker 完成作坊式部署私活级别 Golang 程序 0x01:利用 Docker 构建应用

DevOps1364 字

我们在上一小节实现了应用的自动部署,但是其中有一个小问题,我们的部署依赖于线下打包,然后使用 scp 发给服务器,这个时候如果应用程序体积过大或者网络情况不好,那么就会非常耗时,你不能保证你时时刻刻都在一个非常良好的网络条件下。

这个时候我们就需要使用构建服务器了,但是使用 Jenkins 也需要新的服务器资源才可以,而且还需要进行学习,学习也是一个成本,最近呢,你还比较忙,真是抽不出来时间,那怎么办呢?

其实 Jenkins 的自动部署也是要依赖于我们的脚本的,我们使用 shell 完全可以解决这个问题,毕竟在没有自动化 CICD 之前,大家都是用 shell 解决的。

注意:这里是私活级!!!!

注意:这里是私活级!!!!

注意:这里是私活级!!!!

重要的事情说三遍,在一般的公司不会允许这么搞的!

想要在远程构建,首先我们需要解决代码如何同步到服务器上。

将代码上传至服务器

如果想要在线上进行打包,那么首先就需要将代码上传至服务器。

Goland 是一个强大的 IDE,其中为我们提供了上传代码的功能,这样一来我们就可以达到上传代码到服务器的目的。

在正常的公司里是肯定不允许这样操作的!

首先点击 Tools -> Deployment -> Configuration

2022-10-02T17:18:19.png

点击 + 号 -> sftp -> 输入 sever name

2022-10-02T17:19:17.png

开始填写 SFTP 内容,首先我们要新建 ssh 配置。

2022-10-02T17:20:44.png

因为我们之前已经添加了免密登录,所以我们在添加完IP信息后,选择 key pair ,他会默认帮我们选择好 id_rsa 文件,然后点击 Test Connection 看看能否链接服务器。

2022-10-02T17:20:53.png

回到 Deployment 配置页面我们点击 Root path配置目录,我们输入 /data/data。

2022-10-02T17:21:05.png

接下来点击 Mappings 来配置我们要将代码上传到哪个文件夹下,点击 Deployment path 并点击鼠标右键新建一个 src 的目录用于存放我们的源代码。

2022-10-02T17:21:20.png

然后再点击 Excluded Paths 来过滤掉不想上传的文件。

2022-10-02T17:21:31.png

build,.idea 两个目录其实是不用上传的,点击完成,接下来我们选中main.go 和 go.mod 这两个文件将其上传至 pro。

2022-10-02T17:21:40.png

接下来我们到 Linux 服务器上看一下代码是否已经被上传到服务器上。

[root@localhost src]# cd /data/data/src/ && ll
total 8
-rw-r--r--. 1 root root 1049 Oct  2 13:43 go.mod
-rw-r--r--. 1 root root  253 Oct  2 16:49 main.go

利用 Docker 构建应用程序

我们其实也可以在服务器上安装 golang 然后直接利用 go build 命令来构建应用程序,但是这其实涉及到污染生产环境的问题,所以我们采用 docker 的方式来进行打包,首先我们先安装 docker。

# 更新 yum 
sudo yum update

# 安装依赖
sudo yum install -y yum-utils device-mapper-persistent-data lvm2 -y

# 设置 docker 源
sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo

# 安装 docker
sudo yum install docker-ce -y

# 启动 docker
sudo systemctl start docker

# 随开机启动
sudo systemctl enable docker

# 查看 docker 版本
docker version

因为我们的本地 Golang 用的是1.18.6,所以我们在 docker hub 上选择了 1.18.6-apline3.16 作为我们的打包工具。

选择 alpine 版本还有一个原因就是它的体积是非常小的,既然是“私活”,那就应该从各个方面来思考如何节省资源。

[root@localhost src]# docker pull golang:1.18.6-alpine3.16

如果你觉着 pull 的速度太慢,那可以换成国内的源,这里我就不赘述了。

下载完成后,我们来执行 docker run 命令来打包试试:

[root@localhost src]# docker run --rm -it \
-v /data/data:/data/data \ 
-w /data/data/src/ \
 e CGO_ENABLED=0 \
-e GOPROXY=https://goproxy.cn \
golang:1.18.6-alpine3.16 go build -o ../server-build-docker main.go

go: downloading github.com/gin-gonic/gin v1.8.1
go: downloading github.com/gin-contrib/sse v0.1.0
go: downloading github.com/mattn/go-isatty v0.0.14
go: downloading golang.org/x/net v0.0.0-20210226172049-e18ecbb05110
go: downloading github.com/go-playground/validator/v10 v10.10.0
go: downloading github.com/pelletier/go-toml/v2 v2.0.1
go: downloading github.com/ugorji/go/codec v1.2.7
go: downloading google.golang.org/protobuf v1.28.0
go: downloading gopkg.in/yaml.v2 v2.4.0
go: downloading golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069
go: downloading github.com/go-playground/universal-translator v0.18.0
go: downloading github.com/leodido/go-urn v1.2.1
go: downloading golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97
go: downloading golang.org/x/text v0.3.6
go: downloading github.com/go-playground/locales v0.14.0

--rm 在docker 容器执行完成后会直接被删掉,我们首先是将/data/data 目录挂在到容器中,之后将工作目录切换到 /data/data/src 目录下进行打包操作。

[root@localhost data]# cd /data/data && ls -l
total 9880
-rwxr-xr-x. 1 root   root   10110954 Oct  2 23:42 server-build-docker
-rw-rw-r--. 1 maksim maksim     2783 Oct  2 17:20 server.log
drwxr-xr-x. 2 root   root         49 Oct  2 23:41 src

我们可以看到文件被生成了,并且被设置成了可执行文件。

这里存在一个问题,每一次都会去重新下载依赖,这样会影响打包的速度,我们可以将 gopath 目录找到然后将其目录挂载出来。

# 创建目录用来保存 go build 的依赖文件
mkdir /data/data/godep

# 执行新的 go build 命令
docker run --rm -it -v /data/data:/data/data \ 
-v /data/data/godep:/go \
-w /data/data/src/ \
 e CGO_ENABLED=0 \
-e GOPROXY=https://goproxy.cn \
golang:1.18.6-alpine3.16 go build -o ../server-build-docker main.go

# 第二次执行
docker run --rm -it -v /data/data:/data/data \ 
-v /data/data/godep:/go \
-w /data/data/src/ \
 e CGO_ENABLED=0 \
-e GOPROXY=https://goproxy.cn \
golang:1.18.6-alpine3.16 go build -o ../server-build-docker main.go

在我们第二次执行的时候就不会再去下载依赖了。

改进编译、部署脚本

这个时候我们再去完善我们的部署脚本,我们新建一个 remote-build-deploy 脚本,完成构建以及部署操作。

:: 构建应用
ssh [email protected] "bash /data/data/src/remote-build.sh"

这一次,我们的脚本变成了一个行,所有的编译环节的脚本我们都放到了服务器上,这是因为 ssh 单条执行命令的效率实在是太低了,并且部署脚本单独抽出来,我们可以在其中做一些逻辑判断,如下:

#!/bin/bash

build_path=/data/data/server-build-docker

build() {
  echo "开始执行编译"

  docker run --rm -i \
    -v /data/data:/data/data \
    -v /data/data/godep:/go \
    -w /data/data/src/ \
    -e GOPROXY=https://goproxy.cn \
    -e CGO_ENABLED=0 \
    golang:1.18.6-alpine3.16 go build -o $build_path main.go

  if [ -f $build_path ]; then
    echo "build 成功:目录 "$build_path
  else
    echo "build 失败,没有找到该文件"
        exit 20
  fi
}

build

当完成后,我们去判断是否有文件产出,也就是我们的常说的"制品",如果没有就赶紧报错!

之后就是部署脚本,与构建脚本一样,部署脚本我们也放到了远程执行。

:: 部署应用
ssh [email protected] "bash /data/data/src/remote-deploy.sh"
#!/bin/bash

server_path=/data/server/api-server/server
build_path=/data/data/server-build-docker

deploy() {
  if [ ! -f $build_path ]; then
    echo "build 失败,没有找到该文件"
    exit 20
  fi

  process=$(ps -ef | grep $server_path | grep -v "grep" | wc -l)

  if [[ process == 0 ]]; then
    echo "当前没有执行,可以直接启动,启动中...."
    # 进程不存在直接移动目录4
    mv $build_path $server_path
    nohup $server_path &>>/data/data/server.log 2>&1 &
  else
    echo "当前有执行,杀死进程"
    ps -ef | grep server | awk '{print $2}' | xargs kill -9
    echo "杀死成功,执行运动命令"
    mv $build_path $server_path
    echo "开始执行"
    nohup $server_path &>>/data/data/server.log 2>&1 &
  fi
  echo "执行成功";

}

test() {
    sleep 2
    curl localhost:8080
    echo ""
}

deploy

test
  1. 首先校验的是是否有制品存在
  2. 如果有制品,判断当前是否有正在执行的进程
  3. 如果有在执行的进程,那么就干掉,然后移动、执行。
  4. 如果没有则直接移动、执行

小结

到目前为止,我们完成了自动化远程 docker 编译,在下一小节,我们来实现 api-server 的 docker 化。

maksim
Maksim(一笑,吡罗),PHPer,Goper
OωO
开启隐私评论,您的评论仅作者和评论双方可见