一、采用gvt管理依赖(目前推荐使用 go module 管理依赖)
同java的maven方案一样,go也具备有管理依赖的方案,如godep、gv、gvt、govendor等,在这里我使用的是gvt,在这里说说我是如何使用gvt实现依赖管理的。由于我自己平常都是使用ubuntu做开发和日常使用,这里就只说如何在ubuntu上操作,windows的额我就不说了~
首先安装gvt(这里请自行翻墙)
go get -u github.com/FiloSottile/gvt
进入项目src下,使用以下命令:
$ gvt fetch github.com/BurntSushi/toml
之后在src下面就会看到生成文件夹vendor以及vendor下面生成相应依赖和manifest,之后再多次重复gvt fetch xxx命令即可生成完整依赖(xxx修改为相应的依赖),图示如下:
当采用了gvt管理依赖后,启动项目的时候系统会默认从src/vendor下面寻找相应的依赖。
为了比较完整的玩这一套微服务项目,我引入了docker做容器,并在docker上跑这个服务,这里说说我是如何采用docker做容器的。
具体如何安装docker可以查看官网(https://docs.docker.com/engine/installation/linux/docker-ce/ubuntu/),这里我就不提了,安装成功后,在命令行输入:docker version 有输出版本等信息即意味着安装成功。
我先在项目根目录创建了dockerbase,在里边放置我的Dockefile文件,Dockerfile内容如下,具体的可以查看源码:
FROM ubuntu:14.04 MAINTAINER ricoder "ricoder142@gmail.com" ADD http://mirrors.163.com/.help/sources.list.trusty /etc/apt/sources.list COPY conf/redis.conf /etc/redis/6379.conf COPY conf/consul.json /etc/consul/consul.json COPY supervisor/*.conf /etc/supervisor/conf.d/ RUN apt-get update && / apt-get -y install build-essential && / apt-get -y install openssh-server && / apt-get -y install libssl-dev && / apt-get -y install git && / apt-get -y install vim && / apt-get -y install wget && / apt-get -y install curl && / apt-get -y install unzip RUN apt-get -y install supervisor && / apt-get -y install redis-server && / apt-get -y install mysql-server && / apt-get -y install mysql-client && / mkdir -p /data/services/consul-0.9/bin/ && / wget https://releases.hashicorp.com/consul/0.9.0/consul_0.9.0_linux_amd64.zip && / unzip consul_0.9.0_linux_amd64.zip && / mv ./consul /usr/local/bin/ && / mkdir /data/consul/ && / mkdir -p /data/logs/gologs/ && / mysql_install_db && / update-rc.d -f mysql defaults && / wget https://storage.googleapis.com/golang/go1.9.1.linux-amd64.tar.gz && / tar zxf go1.9.1.linux-amd64.tar.gz && / mkdir -p /data/goapp && / mv go/ /data/services/ && / rm -rf consul_0.9.0_linux_amd64.zip go1.9.1.linux-amd64.tar.gz && / echo "export GOROOT=/data/services/go/nPATH=$PATH:/data/services/go/bin" >> ~/.bashrc EXPOSE 3306 8500 6379 8082 8083 8084 8085 5324 9999 CMD chown -R mysql:mysql /var/lib/mysql && / service mysql start && / supervisord -c /etc/supervisor/supervisord.conf -n
构建的镜像主要实现的功能是:
在ubuntu14.04系统下搭建go生产环境,安装git、vim等工具,安装supervisor管理后台进程,安装redis和mysql,安装consul实现服务发现,暴露部分端口,启动mysql和supervisord等。
Dockerfile涉及的命令有:
(1)FROM(指定基础镜像)
该指令必须指定且需要在Dockerfile其他指令的前面。后续的指令都依赖于该指令指定的image。FROM指令指定的基础image可以是官方远程仓库中的,也可以位于本地仓库。
该指令有两种格式:
b.FROM <image>
指定基础image为该image的最后修改的版本。或者:
a.FROM <image>:<tag>
指定基础镜像为该镜像的一个tag版本。
可以通过我写的Dockerfile看出我指定的基础镜像是14.04 的。
(2)MAINTAINER(用来指定镜像创建者信息)
该指令用于将image的制作者相关的信息写入到image中。当我们对该image执行docker inspect命令时,输出中有相应的字段记录该信息。
指令格式:
MAINTAINER <name>
可以在我写的Dockerfile中看出镜像创建者的信息是:ricoder “ricoder142@gmail.com”
(3)ADD(从src复制文件到容器的dest路径)
目录下。
指令格式:
ADD <src> <dest>
是相对被构建的源目录的相对路径,可以是文件或目录的路径,也可以是一个远程的文件url;
可以在Dockerfile中看出我主要是将conf下和supervisor下的文件复制容器内,复制conf的目的是修改配置,而复制supervisor的目的我会在第三章节说到。
(4)COPY
COPY的语法与功能与ADD基本相同,不同的是不支持上面讲到的
可以在我写的Dockerfile中看出,我是将conf和supervisor文件夹下的文件拷贝到指定的路径,conf下的是对redis和consul的设置,而supervisor的主要实现的是后台进程的管理,等等我会在第三部分做个详细的讲解,此处先略过。
(5)RUN
RUN指令会在当前镜像的顶层执行任何命令,并commit成新的(中间)镜像。
指令格式:
RUN <commnad>或RUN ["executable", "param1", "param2"]
第一种格式是shell格式,相当于执行/bin/sh -c “
RUN apt-get -y install supervisor
exec格式,不会触发shell,所以$HOME这样的环境变量无法使用,但它可以在没有bash的镜像中执行,而且可以避免错误的解析命令字符串:
RUN ["apt-get", "install", "supervisor", "-y"]或RUN ["/bin/bash", "-c", "apt-get install -y supervisor"]
(6)EXPOSE
EXPOSE指令会告诉容器在运行时要监听的端口,不过这个端口是用于多个容器之间通信用的,外面的host是访问不到的。而要把端口暴露给外面的主机,在启动容器时使用-p
选项,这一点在后面将会有提及。示例:
EXPOSE 3306 8500 6379 8082 8083 8084 8085 5324 9999
(7)CMD
一个Dockerfile里只能有一个CMD,如果有多个,只有最后一个生效。CMD指令的主要功能是在build完成后,为了给docker run启动到容器时提供默认命令或参数,这些默认值可以包含可执行的命令,也可以只是参数(此时可执行命令就必须提前在ENTRYPOINT中指定)。
CMD与RUN的区别在于,RUN是在build成镜像时就运行的,而CMD指令实在build完成后运行的,所以RUN先于CMD运行,其次CMD会在每次启动容器的时候运行,而RUN只在创建镜像时执行一次。
可以从我的Dockerfile中看出在这里CMD的作用是启动mysql和supervisord。
使用命令:
docker build -t gomicro-env .
可以在命令行中看到输出:
ricoder# docker build -t gomicro-env .Sending build context to Docker daemon 42.5 kBStep 1/10 : FROM ubuntu:14.04 ---> dea1945146b9Step 2/10 : MAINTAINER ricoder "ricoder142@gmail.com" ---> Running in d4da66553673 ---> 2e8b5ccd3c9e .......
构建成功后,在命令行输入:
ricoder# docker images
可以在列表中看到:
gomicro-env latest 9c9883edc1db 20 hours ago 899 MB
使用命令:
docker run --name=$Container -p 18087:8082 -p 18505:8500 -d -v `pwd`:/data/deploy/$ProjectName gomicro-env
查看容器:
ricoder# docker container ls
可以在命令行中看到容器的详细信息。
这里大概讲解下docker run这一行话的意思,—name是容器名的参数,-p 对应着的是本地端口映射到容器端口,这里的-p 18087:8082意味着本地的18087映射到8082端口,这样在本地访问18087端口的时候可以访问到容器的8082端口,-d意思是后台运行容器,并返回容器ID,-v参数的给容器添加一个数据卷,即将本地的项目挂载到容器中。
我在dockerbase目录下面新加了一个init.sh文件,内容如下:
!/bin/bash mysql -u root -e "CREATE DATABASE gomicro " R=/data/deploy/gomicro cd $Rbash build_local.sh all
然后运行如下命令:
docker exec $Container bash /data/deploy/$ProjectName/dockerbase/init.sh
该命令的功能是在容器$Container中跑起这个init.sh脚本,实现的功能是创建数据库gomicro和跑起build_local.sh脚本并且携带参数all,接下来看看build_local.sh脚本
#!/bin/sh if [ $1 == "all" ]; then for srv in `ls src`; do if [ ${srv:0-4} == "-srv" ]; then echo "开始更新$srv" GOROOT=/data/services/go GOBIN=/data/goapp/wolf/bin GOPATH=`pwd`:`pwd`/vendor /data/services/go/bin/go install $srv && sudo supervisorctl restart wolf-$srv:* fi done else for srv in "$@" do if [ "${srv:0-4}" != "-srv" ]; then srv="${srv}-srv" fi echo "开始更新$srv" GOROOT=/data/services/go GOBIN=/data/goapp/wolf/bin GOPATH=`pwd`:`pwd`/vendor /data/services/go/bin/go install $srv && sudo supervisorctl restart wolf-$srv:* done fi
从脚本中可以看出,当携带的参数是all的时候实现的功能是 install go服务和使用supervisorctl重启指定的后台进程,至于supervisor如何管理后台进程我会在第三章节说到。
Supervisor是一个 Python 开发的 client/server 系统,是一款Linux下的进程管理软件,可以管理和监控类 UNIX 操作系统上面的进程。它可以同时启动,关闭多个进程,使用起来特别的方便,最主要的两个功能是:
将非daemon程序变成deamon方式运行
对程序进行监控,当程序退出时,可以自动拉起程序
supervisor 主要由两部分组成:
supervisord(server 部分):主要负责管理子进程,响应客户端命令以及日志的输出等;
supervisorctl(client 部分):命令行客户端,用户可以通过它与不同的supervisord 进程联系,获取子进程的状态等。
由于我是在docker容器内部署项目,所以supervisor也是在容器内安装和使用,不过本地也是一样的。可以在我写的dockerfile中看到:
RUN apt-get -y install supervisor && /
我在容器中安装了supervisor,然后在dockerfile中采用以下命令启动supervisor:
supervisord -c /etc/supervisor/supervisord.conf -n
我们可以先使用以下命令登录docker :
docker exec -it $Container /bin/bash
登录成功后采用一下命令查看supervisor是否启动成功:
ps -ef | grep supervisor | grep -v grep
root 448 1 0 15:04 ? 00:00:00 /usr/bin/python /usr/bin/supervisord -c /etc/supervisor/supervisord.conf -n
证明启动成功。
首先,我在Dockerfile中做了如下操作:
COPY supervisor/*.conf /etc/supervisor/conf.d/
将以下的文件全都复制到容器的 /etc/supervisor/conf.d/
文件夹下面
以api-srv.conf为例,目的是将api-srv微服务进程交给supervisor管理,源码如下:
[program:class-api-srv] command=/data/goapp/gomicro/bin/api-srv process_name=class-user-srv autorestart=true redirect_stderr=true stderr_logfile=/data/logs/api-srv.err.log stdout_logfile=/data/logs/api-srv.out.log user=root
command 启动命令 process_name 进程的名字,这里和program一致 autorestart=true 程序异常退出后自动重启 stderr_logfile stderr 日志输出位置 stdout_logfile stdout 日志输出位置
重新启动supervisor
supervisord -c /etc/supervisor/supervisord.conf -n
使用以下命令查看已经在跑的并且被supervisor管理的进程:
supervisordctl status
结果如下:
root@133f583a598b:/# supervisorctl status api-srv RUNNING pid 544, uptime 0:00:12 consul-server RUNNING pid 543, uptime 0:00:12 redis-6379 RUNNING pid 545, uptime 0:00:12
可以看出目前被supervisor管理的进程有三个。
为要管理的进程按照3.3中写到的那样,添加一份conf文件,并放在supervisor目录下面,之后在docker容器内运行命令:
supervisorctl reload
即可管理新增进程。
为了开发方便,我采用了shell脚本部署项目、操作docker、操作数据库等,具体可以查看ctrl.sh文件、build_local.sh文件、build_proto.sh文件和init.sh文件。
这是go微服务系列的第四篇,接下来我会将该技术真正用到项目开发中,开发电影票在线购票系统(毕设),具体可以关注项目:
https://github.com/wiatingpub/MTBSystem ,不介意的话也可以给个star哈。
tip:该项目的源码(包含数据库的增删查改的demo)可以点击原文链接查看源代码~