Docker 完成作坊式部署私活级别 Golang 程序 0x00:基础服务发布

DevOps1930 字

前言

大家好,我是一笑,在这篇文章中将为大家介绍如何利用 Docker 部署私活级 Golang 程序,最终我们要通过最低的成本来实现 goalng 的自动打包,部署操作。

首先我们来定义一下什么是私活级,私活大家都懂,就是我们利用业余去赚一些外快,一般情况下后端基本上都是一个人独立完成的,有的时候甚至真的只有一个人,没有运维,没有测试,一个人要负责整个软件的生命周期,从开发迭代到后面的运维部署,所以在本文中私活级将指代是没有企业级运维能力的研发场景

在这种情况,我们可以利用 Docker 和 脚本命令来完成一些简单的自动化操作,来减少我们的时间成本以及运维成本。

由于刚开始是一个人进行研发工作,所以我们并不会涉及到 gitlab, Jenkins 这些 DevOPS 工具,毕竟这些会增加我们的研发成本,如果非要部署这些服务,我的建议是可以使用树莓派,同时将 gitlab 更换为 gitea ,因为 gitea 可以更加节省服务器资源,同时一定要定时备份,这点很重要,以免树莓派损坏导致的数据丢失。

在后面我会不断追加这一系列的文章,其中也会讲到 DevOPS , 不过这些不是这篇文章的主要内容,在这里不做过多说明,接下来让我们正式开始私活级 Golang 程序的部署工作。

环境准备

这里为了节省成本,就不从云服务厂商购买服务器了,使用虚拟机来进行搭建。

  • Golang 1.18
  • Goland IDE
  • VitralBox
  • Windows(我本机使用的是 Windows 11)
虚拟机操作系统网络模式虚拟机 IP虚拟机 配置
CentOS 7桥接网卡192.168.1.100cup: 2 mem:2G

基础 Go 服务发布

代码准备

首先,我们来初始化我们的项目代码,因为这里的项目并不重要,所以我们用 gin 框架简单输出一个 Hello World 来模拟我们的项目代码,项目结构如下:

PS C:\Users\maksim\WorkSpace> tree .\proejct\
文件夹 PATH 列表
卷序列号为 5402-60A2
│  go.sum
│  main.go

main.go:

package main

import (
    "github.com/gin-gonic/gin"
    "log"
)

func main() {
    r := gin.Default()
    r.Handle("GET", "/", func(context *gin.Context) {
        context.String(200, "hello world!")
    })
    err := r.Run(":8080")

    if err != nil {
        log.Fatalln(err)
    }
}

代码很简单,就是利用 gin 返回 hello world!,我们将以此为基础来进行部署,首先我们不使用 docker 进行部署,而是通过 ssh 将 编译好的代码同步到服务器上。

由于我的开发机是 Windows, 所以这个时候需要进行交叉编译,在Windows 平台上编译 Linux 可执行文件,我们先来编写一个 build.bat 脚本,用来辅助我们自动化生成可执行文件,其脚本路径与 mian.go 同级。

set GOOS=linux
set GOARCH=amd64
go build -o build/server main.go

我们来执行一下脚本文件。

# 执行 build 脚本生成可执行文件
PS C:\Users\maksim\WorkSpace\proejct> .\build.bat

C:\Users\maksim\WorkSpace\proejct>set GOOS=linux

C:\Users\maksim\WorkSpace\proejct>set GOARCH=amd64

C:\Users\maksim\WorkSpace\proejct>go build -o build/server main.go

# 查看 build 目录下文件

PS C:\maksim\guozh\WorkSpace\proejct> ls .\build\


    目录: C:\Users\maksim\WorkSpace\proejct\build


Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
-a----        2022-10-02     14:00       10062347 server

部署 Linux

我的 Linux 安装选用了最小化安装,简单配置了IP,没有对 Linux 进行用户设置,所以用 root 用户进行登录,所以为了安全第一件事就是为 Linux 设置新的账户。

# 1. ssh 登录
ssh [email protected]

# 2. 输入密码,这里略过

# 3. 新建账户
# 3.1 新增用户
useradd -m maksim
# 3.2 给用设置密码
passwd maksim

# 3.3 给 maksim  root 权限
cat >> /etc/sudoers <<EOF              #添加用户可以知晓所有命令(可以根据需要修改),切换不用输入密码
user1   ALL=(ALL)      NOPASSWD: ALL
EOF

# 4. 给新用户 SSH 登录权限
# 4.1 编辑 sshd_config 文件
vi /etc/ssh/sshd_config
# 4.2 找到 AllowUsers, 解除注释,并且加入用户名,用户名之间用空格隔开
AllowUsers username username2
# 4.3 重启 ssdh 服务
service sshd restart

这样主要是为了 Linux 的系统安全,在线上拥有 root 权限其实是一个非常可怕的情况,毕竟如果你想的话 rm -rf /* 也是完全没有问题的,所以一般公司都不会给开发人员 root 权限,线上一般也就是给只读权限的账户。

现在我们已经拥有了新的账户,接下来我们来部署应用,现在使用新的账户进行操作。

# 在根目录下新建data 目录,其中包括 soft,data,server
sudo mkdir -p /data/{soft,data,server}

[maksim@localhost data]$ cd /data/ && ls -l
total 0
drwxr-xr-x. 2 root root 6 Oct  2 14:29 data
drwxr-xr-x. 2 root root 6 Oct  2 14:29 server
drwxr-xr-x. 2 root root 6 Oct  2 14:29 soft
  • soft 目录是用来存放我们安装的应用,例如 redis, MySQL, nginx
  • data 用来存放应用数据,例如Nginx 日志,应用日志等。
  • server 用来存放我们的应用服务,也就是我们要部署的内容;

我们在 soft 目录下新建 api-server 目录,该目录用来存放我们的 helloworld 程序。

cd server  && sudo mkdir api-server

创建完目录后,我们来设置免密登录,这是因为我们需要上传我们的应用程序,如果是使用 scp 上传每一次都需要传入密码,就很麻烦。

首先我们打开 cmd 生成本机的 ssh 密匙。

PS C:\Users\maksim>  ssh-keygen -t rsa -C "[email protected]"
Generating public/private rsa key pair.
Enter file in which to save the key (C:\Users\maksim/.ssh/id_rsa):
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in C:\Users\maksim/.ssh/id_rsa
Your public key has been saved in C:\Users\maksim/.ssh/id_rsa.pub
The key fingerprint is:
SHA256:pwc27Wi3bpeGM8vZJ6MoM2Hoz5MWMU1SZdMl3cfVYLY [email protected]
The key's randomart image is:
+---[RSA 3072]----+
|       ...+..o=++|
|      . .. ..+..=|
|       +      E .|
|      o ..       |
|     . oS o      |
|    . +. B       |
|   . . ++ +. .   |
|    ..B. ==+* .  |
|     oo=.oBB.+   |
+----[SHA256]-----+

ssh-keygen -t rsa -C "[email protected]" 这个命令的具体意义,大家可自行百度,这里不做赘述,因为这篇文章的重点不在这里。

 PS C:\Users\maksim\.ssh> type .\id_rsa.pub

ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQC8gUVLCs1Vbei3L6lYWrr7QTWee/qEnC9t2oNeiSKsQMtRTRUh+oCIqSR6zL+5giK1bthRUJnKaJZqrJPSsdaiispaoduoiIsklfdjJLIOJI@saslE= [email protected]

我们可以到查看该目录下的 ssh ,在这里的ssh 请不要复制,因为是我瞎写的。

我们将生成好的 id_rsa.pub 文件内容添加到 Linux 中。

PS C:\Users\maksim\.ssh> type ./id_rsa.pub | ssh [email protected] "mkdir .ssh && cat >> .ssh/authorized_keys"
注意:这里的脚本中有一段是 mkdir .ssh 这是因为我的虚拟机中没有该目录,如果报错了的话,可以将 makdir .ssh && 删掉。

在这里我们使用了 root 账户,这是因为可以避免很多权限问题。

我们来测试一下是否可以免密码登录:

PS C:\Users\maksim\.ssh> ssh [email protected]
Last login: Sun Oct  2 16:18:41 2022
[root@localhost ~]#

现在我们来编写 deploy.sh 脚本,用来部署我们的应用程序:

scp ./build/server root:/data/server/api-server/server

我们通过 scp 将编译好的目录上传上去,我们执行完成后就会发现,server 文件就被上传到了我们的Linux当中:

[root@localhost api-server]# ls -l
total 9828
-rw-r--r--. 1 root root 10062347 Oct  2 16:39 server

但是这个时候,细心的你发现这个文件没有可执行权限啊,很简单,我们来继续晚上 deploy.bat 脚本,我们使用 ssh 命令 来给该文件配置权限。

set server_path=/data/server/api-server/server

scp ./build/server [email protected]:%server_path%

set chmod_exec_cmd= chmod +x %server_path%

ssh [email protected] %chmod_exec_cmd%

这是我们修改后的脚本,使用 bat 变量来配置要上传的路径,并且在最后执行了权限修改命令,我们来执行一下,再看看 Linux 中 server 的变化。

[root@localhost api-server]# ls -l
total 9828
-rwxr-xr-x. 1 root root 10062347 Oct  2 16:45 server

我们可以看到,这样一来 server 就具备了可执行权限,我们可以执行一下 server。

[maksim@localhost api-server]$ ./server
[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.

[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
 - using env:   export GIN_MODE=release
 - using code:  gin.SetMode(gin.ReleaseMode)

[GIN-debug] GET    /                         --> main.main.func1 (3 handlers)
[GIN-debug] [WARNING] You trusted all proxies, this is NOT safe. We recommend you to set a value.
Please check https://pkg.go.dev/github.com/gin-gonic/gin#readme-don-t-trust-all-proxies for details.
[GIN-debug] Listening and serving HTTP on :8080

我们先让它在后台运行

#我们现在用的是 maksim 账户,但是创建目录的时候用的 root ,所以我们需要将权限转交给 maksim 避免出现权限错误
[maksim@localhost api-server] chown -R /data/data maksim

[maksim@localhost api-server] nohup ./server &>> /data/data/server.log 2>&1 &

运行后我们执行 curl 命令来判断程序是否运行成功:

[maksim@localhost api-server]$ curl localhost:8080
hello world!

在这里还有一点需要进行优化,那就是我们的服务如果在运行阶段是无法重新部署的,我们可以试验一下:

[maksim@localhost api-server]$ ls -l
total 9828
-rwxr-xr-x. 1 root root 10062347 Oct  2 17:10 server

现在最新的上传时间是17:10,我们来执行上传脚本,然后在观察时间的变化。

PS C:\Users\maksim\WorkSpace\proejct> .\delopy.bat

C:\Users\maksim\WorkSpace\proejct>set server_path=/data/server/api-server/server

C:\Users\maksim\WorkSpace\proejct>scp ./build/server [email protected]:/data/server/api-server/server
scp: /data/server/api-server/server: Text file busy

C:\Users\maksim\WorkSpace\proejct>set chmod_exec_cmd= chmod +x /data/server/api-server/server

C:\Users\maksim\WorkSpace\proejct>ssh [email protected]  chmod +x /data/server/api-server/server

我们可以看到,这个时候的脚本执行是没有报错的,但是我们在 Linux 中执行 ls -l

[maksim@localhost api-server]$ ls -l
total 9828
-rwxr-xr-x. 1 root root 10062347 Oct  2 17:10 server

所以我们需要将 Linux 中的进程杀死,然后在重新启动脚本。

:: 设置服务文件路径
set server_path=/data/server/api-server/server

:: 将进程杀死
ssh [email protected] "ps -ef | grep server | awk '{print $2}' | xargs kill -9"

:: 发送应用程序文件
scp ./build/server [email protected]:%server_path%

:: 设置执行权限
set chmod_exec_cmd= chmod +x %server_path%
ssh [email protected] %chmod_exec_cmd%
ssh [email protected] "nohup %server_path% &>> /data/data/server.log 2>&1 &"

我们执行完成后再去 Linux 上看一下。

[maksim@localhost api-server]$ ps -ef | grep server
root      2210     1  0 17:19 ?        00:00:00 /data/server/api-server/server
maksim    2216  1867  0 17:19 pts/1    00:00:00 grep --color=auto server
[maksim@localhost api-server]$ curl localhost:8080
hello world!

好了,到目前为止,我们的开发,打包,部署就已经基本完成了,下面我们开始对项目进行 docker 化,如果你不想打包 docker 那么到这里你还有以下几点需要去做:

  1. 使用 supervisor 监控应用程序
  2. 如果真的只有一台节点,那么最好做应用的优雅重启,避免在部署阶段导致服务中断,而与此同时,还需要改造 deploy.sh 以支持优雅重启。

小结

在这一篇文章讲解了,如何利用 bat 脚本、ssh 命令来部署我们的 golang 应用程序。

其实在工作中,我真的有遇到过这种发布模式,很简单,几行脚本就搞定了大套的发布流程,看起来很作坊,很不成体系,但是确实成本最低的实现方案。

在下一章中我们将开始利用 Docker 来部署我们的项目。

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