作者 青鸟

在试过了systemed部署服务之后,尝试了一下docker来部署,相较于systemed的部署,docker更加优雅和方便,但在这个过程中也是遇到了大大小小的问题,也是做了很多优化,特地记录一下,值得注意的是我没有使用docker-compose来部署,而是单纯的容器间通讯,之后会再写一篇来记录。文章较长,有错误的欢迎指出

关于docker环境的安装,按照官方教程即可以轻松解决,同时需要注意Docker Hub被墙了,因此要用镜像源,推荐使用腾讯云的镜像仓库

搭建生产环境

这里的生产环境主要指的就是redis和mysql这两个数据库,这里有一个大坑就是当你卸载容器的时候,数据库里的数据会随之销毁,这显然是不符合我们的要求的,解决办法就是配置的时候使用挂载数据卷的方式,将数据库信息挂在到宿主主机上,以此可以在销毁或者重启容器的时候做到数据持久化。

mysql配置

我们首先创建三个文件夹

1
2
3
4
5
6
#配置文件
mkdir /usr/local/mysql/conf
#数据存储
mkdir /usr/local/mysql/data
#mysql日志
mkdir /usr/local/mysql/logs

首先编辑配置文件

创建一个my.cnf

1
2
3
4
5
6
7
8
9
10
11
12
13
14
[mysqld]
pid-file = /var/run/mysqld/mysqld.pid
socket = /var/run/mysqld/mysqld.sock
datadir = /var/lib/mysql
secure-file-priv= NULL
# Disabling symbolic-links is recommended to prevent assorted security risks
symbolic-links=0
character-set-server=utf8
[client]
default-character-set=utf8
[mysql]
default-character-set=utf8
# Custom config should go here
!includedir /etc/mysql/conf.d/

配置文件之所以不采用容器内部的,是因为可能存在中文编码的问题,稳妥起见还是自己写一个配置让人放心

接下来,我们直接把mysql的Image给pull下来,然后run一下就好了

1
docker run --restart=always -d -v /usr/local/mysql/conf/my.cnf:/etc/mysql/my.cnf -v /usr/local/mysql/logs:/logs -v /usr/local/mysql/data/mysql:/var/lib/mysql  -p 3306:3306 --name mysql -e MYSQL_ROOT_PASSWORD=123456 mysql

之后就像正常的数据库一样,做一个简单的配置,开一个用户可以对外连接的即可

redis配置

1
2
3
4
5
6
#配置文件
mkdir /usr/local/mysql/conf
#数据存储
mkdir /usr/local/mysql/data
#mysql日志
mkdir /usr/local/mysql/log

我们从网上找一份redis.conf配置,复制下来即可

这里要注意几个很坑的点

1
2
3
4
5
6
1、允许redis外地连接,这里的本地并不是宿主计算机的本地,docker使用了Linux的Namespaces技术来进行资源隔离,其中就包括了网络环境,一个Network Namespace提供了一份独立的网络环境,包括网卡、路由、Iptable规则等都与其他的Network Namespace隔离。
注释掉 # bind 127.0.0.1
2、修改daemonize 这个是个很坑的点,就是daemonize是代表开启守护进程模式,这和docker的-d是冲突的,会导致容器启动失败
daemonize no
3、开启redis数据持久化
appendonly yes

然后在log中新建一个redis.log即可

最后从Hub中pull下redis的Image,run一下即可

1
docker run --restart=always -d --privileged=true -p 6379:6379 -v /usr/local/docker/redis/conf/redis.conf:/etc/redis/redis.conf  -v /usr/local/docker/redis/log/redis.log:/var/log/redis.log -v /usr/local/docker/redis/data:/data --name redis redis redis-server /

将Goland程序打包成Image

我们先将程序打包成所需的二进制文件,然后用sftp将二进制文件和配置文件一起上传到服务器上的文件下

这里需要说明的是为什么打包二进制,很多网上的文章都是在容器内部打包的,其实我们去查看内存大小会发现容器的磁盘占用是很大的,但是其实最后运行的只有一个二进制文件,同时我们还在镜像中调用了goland镜像,这显然不是我们运行所必需的,因为build会打包成所需的动态连接库,所以go的环境就不需要了。我们直接引入scratch这个空镜像就OK了

打包的时候可以使用一下go命令打包

1
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 GIN_MODE=release go build -o example

然后在这个文件夹下创建一个dockerfile

1
2
3
4
5
6
FROM scratch
MAINTAINER cbluebird
WORKDIR /app
COPY example /app
EXPOSE 6060
CMD ["./example"]

然后在bash中执行下面的命令

1
docker build -t example .

然后使用下面的命令查看Image是否存在

1
docker images

查看到example已经在Image已经在本地了,之后运行即可

运行Image

yaml的配置

首先我们要注意的是在yaml配置中mysql和redis的配置,我们不能简单的理解出来为本地环境localhost,与之前redis中提到的一样,docker使用了Linux的Namespaces技术来进行资源隔离,其中就包括了网络环境,一个Network Namespace提供了一份独立的网络环境,包括网卡、路由、Iptable规则等都与其他的Network Namespace隔离。

这里主要有四种解决办法

第一种也就是最直接的,直接替换掉本地的localhost,换成docker分配的IPAddress即可,我们使用下面命令即可查看容器的IPAddress

1
docker inspect <container_id> | grep IPAddress

第二种办法是在运行时使用--link来连接,比如--link mysql:mysql就可以在这个容器里使用mysql的本地了

第三种方法是使用host,这样子就和宿主机处于同一个网络下了,容器将不会虚拟出自己的网卡,配置自己的IP等,而是使用宿主机的IP和端口。但是这种方法显然丧失了使用docker很重要的原因,容器的隔离性和安全

第四种方法是自定义网络,将几个容器都放在一个虚拟网络环境下,这就比较麻烦了,感兴趣的可以自己google

挂载yaml

其实我们也可以将yaml打包进docker中,但是这样子将不利于我们配置的更改,因为我们一般的查看和更改文件都是需要进入到容器中,我们一般使用下面命令进入

1
docker exec -it mysql /bin/bash

但是这里就存在了一个问题就是我们拿什么去修改呢,我们在打包的时候其实并没有将编辑器打包进去,这就导致了修改的难度很大,于是一个比较优雅的方式就是使用数据卷挂载,这样子就可以将配置文件存放在宿主机中来维护,我们在run的时候使用-v即可

运行容器

说完了上述两个坑,我们就可以直接运行了

1
run --privileged=true -p 6060:6060 -v /opt/example/config.yaml:/app/config.yaml --name example example

其中的几个配置需要说明一下

1
2
3
4
5
-v 是数据卷挂载
-p 端口映射,将容器的端口映射到宿主机的端口上,来和外界通讯
--name 容器别名
--privileged 是否使用管理员权限
--restart 是否自动启动,使得容器具有自愈功能,可以开机自动启动

最后我们使用

1
docker logs -f user-center

就可以查看容器的日志了,至此一个docker部署的应用就优雅地跑起来了

Docker常用的命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# 停止一个容器
docker stop wejh

#运行一个容器
docker start exapmle

#重新运行一个容器
docker restart example

#从本地删除一个容器
docker rm example

#从本地删除一个镜像
docker rmi example

#查看容器的日志
docker logs -f example

#通过一个bash进入一个容器内部
docker exec -it mysql /bin/bash

#查看所有在运行的容器
docker ps

#查看所有容器
docker ps -a

#查看本地所有的镜像
docker images

#打包成镜像
docker build -t example .

# 查看m容器的IPAddress
docker inspect <container_id> | grep IPAddress

#会以 json 格式得到 docker 镜像/容器的元数据
docker inspect example

文章较长,有错误的欢迎指出

参考文章:
Docker 网络及通信方式

添加镜像仓库

官方下载教程