Docker 是一个开源的轻量级容器技术,让开发者可以打包他们的应用以及应用运行的上下文环境到一个可移植的镜像中,然后发布到任何支持Docker的系统上运行。 通过容器技术,在几乎没有性能开销的情况下,Docker 为应用提供了一个隔离运行环境.

Docker优点

  • 简化配置
  • 代码流水线管理
  • 提高开发效率
  • 隔离应用
  • 快速、持续部署

Golang 支持交叉编译,在一个平台上生成另一个平台的可执行程序;我建议将不同的依赖放在不同的docker容器中,如mysql、redis等放在单独的一个容器中。

编写golang 项目

传统项目一般包含了mysql,redis的应用,接下来,我们编写一个简单的web-app项目

app.go

package main

import (
    "fmt"
    "github.com/garyburd/redigo/redis"
    "github.com/jinzhu/gorm"
    _ "github.com/jinzhu/gorm/dialects/mysql"
    "net/http"
    "os"
    "strconv"
    "strings"
)

const (
    MaxIdle     = 16
    MaxActive   = 1024
    IdleTimeout = 300
)
const (
    MysqlMaxLifetime = 10 * 60 * 1000
    MysqlMaxOpenConn = 50
    MysqlMaxIdleConn = 1000
)

type User struct {
    ID   int64
    Name string
}

//根据id查询用户
func GetUserById(id int64) (*User, error) {
    var user User
    err := DB.Table("user").Where("id = ?", id).First(&user).Error
    return &user, err
}

var redisPool *redis.Pool

func InitRedis() {
    //获取系统环境变量REDIS_PORT
    addr := strings.Split(os.Getenv("REDIS_PORT"), "://")
    redisPool = &redis.Pool{
        MaxIdle:     MaxIdle,
        MaxActive:   MaxActive,
        IdleTimeout: IdleTimeout,
        Dial: func() (redis.Conn, error) {
            return redis.Dial(addr[0], addr[1])
        },
    }
}

var DB *gorm.DB

func InitDB() (*gorm.DB, error) {
    //获取系统环境变量MYSQL_PORT
    addr := strings.Split(os.Getenv("MYSQL_PORT"), "://")
    fmt.Println(addr)
    msqlUrl := fmt.Sprintf("root:123456@%s(%s)/%s?charset=utf8&parseTime=True", addr[0], addr[1], "test")
    fmt.Println(msqlUrl)
    db, err := gorm.Open("mysql", msqlUrl)
    if err != nil {
        fmt.Println("mysql链接失败", err)
        return nil, err
    }
    db.DB().SetConnMaxLifetime(MysqlMaxLifetime)
    db.DB().SetMaxIdleConns(MysqlMaxIdleConn)
    db.DB().SetMaxOpenConns(MysqlMaxOpenConn)
    DB = db
    return db, nil
}

func RedisSET(key string, value interface{}, t int32) string {
    conn := redisPool.Get()
    defer conn.Close()
    var d string
    var err error
    if t == 0 {
        d, err = redis.String(conn.Do("SET", key, value))
    } else {
        d, err = redis.String(conn.Do("SET", key, value, "EX", t))
    }
    if err != nil {
        fmt.Println("redis连接错误", err)
        return ""
    }
    return d
}

func RedisGET(key string) []byte {
    conn := redisPool.Get()
    defer conn.Close()
    d, err := redis.Bytes(conn.Do("GET", key))
    if err != nil {
        fmt.Println("redis错误", err)
        return nil
    }
    return d
}

func IndexHandler(w http.ResponseWriter, r *http.Request) {
    count := string(RedisGET("view_count"))
    c, e := strconv.Atoi(count)
    if e != nil {
        c = 0
    } else {
        c++
    }
    RedisSET("view_count", c, 0)
    user, error := GetUserById(1)
    if error == nil {
        fmt.Fprintf(w, "%s say hello golang = %v\n", user.Name, c)
    }
}

func main() {
    InitDB()
    InitRedis()
    http.HandleFunc("/", IndexHandler)
    http.ListenAndServe("0.0.0.0:8080", nil)
}

编译项目

go build app.go

编写Dockerfile

然后参考官方的文档弄了下Dockerfile大概是这样:

FROM scratch
ADD app /
CMD ["/app"]

这里解释下:

  • FROM 是集成自哪个镜像,我们是go程序官方提供了一个golang这样的镜像,我们可以直接使用。
  • ADD 添加并解压,我们是将app添加到容器的根目录。
  • CMD 设置容器启动后默认执行的命令和参数

编译Dockerfile

基于Dockerfile我们构建一个叫diycoder/web-app 的镜像,命令如下

sudo docker build -f Dockerfile -t diycoder/web-app .

编译项目的时候,我们的app文件生成的时候依赖的一些库如libc还是动态链接的,但是scratch 镜像完全是空的,什么东西也不包含,所以生成app时候要按照下面的方式生成,使生成的app静态链接所有的库:

开启交叉编译,需要设置GOOS

CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app .

注意:其实主要的是使用 CGO_ENABLED=0 ,关闭cgo

删除并重新构建镜像

sudo docker rmi [diycoder/web-app镜像id]
sudo docker build -f Dockerfile -t diycoder/web-app .

配置并运行MySQL

sudo docker run -p 3306:3306 --name mysql -v /home/vagrant/docker/mysql/conf:/etc/mysql/conf.d -v /home/vagrant/docker/mysql/logs:/logs -v /home/vagrant/docker/mysql/data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 -e MYSQL_DATABASE=webapp -d mysql:5.7

命令说明

-p     3306:3306:将容器的 3306 端口映射到主机的 3306 端口。
-v     /home/vagrant/docker/mysql/logs:/logs 将docker数据库日志文件存放在此
-v     /home/vagrant/docker/mysql/data:/var/lib/mysql :将主机当前目录下的data目录挂载到容器的 /var/lib/mysql 。
-v     /home/vagrant/docker/mysql/conf:/etc/mysql/conf.d :将mysql配置文件挂载到docker容器里 。
-e     MYSQL_ROOT_PASSWORD=123456:初始化 root 用户的密码。
-e     MYSQL_DATABASE=webapp:初始化 MySQL 数据库。

导入数据库文件

格式:
docker exec -i [容器名称/容器id] mysql -uroot -p数据库密码 数据库名称 < 导入的数据库文件路径

我们先来看一下数据库文件webapp.sql

use webapp;
create table user(id int(2),name varchar(12));
insert into user(id,name) values(1,'mumu');
sudo docker exec -i mysql mysql -uroot -p123456 webapp < webapp.sql

配置并运行Redis

sudo docker run --name redis -v /home/vagrant/docker/redis/data:/data -v /home/vagrant/docker/redis/conf/redis.conf:/usr/local/etc/redis/redis.conf  -p 6379:6379 -d redis:3.2 redis-server /usr/local/etc/redis/redis.conf --appendonly yes

命令说明

-p     6379:6379:将容器的 6379 端口映射到主机的 6379 端口。
-v     /home/vagrant/docker/redis/data:/data :将主机当前目录下的data目录挂载到容器的 /data 。
-v     /home/vagrant/docker/redis/conf/redis.conf:/usr/local/etc/redis/redis.conf  :将redis配置文件挂载到docker容器里 。

配置并运行Golang项目所在容器

sudo docker run -d --name web-app --link mysql --link redis -p 8080:8080 diycoder/web-app

命令说明:

--link     可以用来链接2个容器,使得源容器(被链接的容器)和接收容器(主动去链接的容器)之间可以互相通信,并且接收容器可以获取源容器的一些数据,如源容器的环境变量

我们进入web-app容器来看下

sudo docker exec -it web-app /bin/bash

我们来看下环境变量信息

Docker 实践 —— 部署golang项目

发现没有,这就是我们再golang项目中os.Getenv()要获取的环境变量信息

项目测试

curl 127.0.0.1:8080

多执行几次,结果如下,说明项目部署成功了

[root@swarm-manager webapp]# curl 127.0.0.1:8080
mumu say hello golang = 11
[root@swarm-manager webapp]# curl 127.0.0.1:8080
mumu say hello golang = 12
[root@swarm-manager webapp]# curl 127.0.0.1:8080
mumu say hello golang = 13

参考链接:
Building Minimal Docker Containers for Go Applications
创建超小的Golang docker 镜像

文章目录