最近很长一段时间一直经过各类渠道去了解国内外有关devops方面的实践,感觉不少的知识点都特别的分散,因此想经过系统的整理来巩固相应的知识体系。接下来会撰写一系列有关容器化的文章。
一、 利用docker部署一个简单的go程序,而且利用阿里云的平台,进行镜像的生成
二、 利用docker-compose部署一个复杂的go程序,同时部署包含多个不一样子容器的集成
三、 利用gitlab和Harbor来作ci/cdlinux
容器是独立的软件包,可以将应用程序以及全部的依赖项、工具、库、配置文件 以及运行该应用程序所须要的全部的其余内容捆绑在一块儿。
经过docker能够实现将您 的应用环境从运行的物理操做系统上面抽象一层虚拟操做环境。从而保证你的程序从开发到测试以及现场的部署,保证了环境 的一致性问题。git
在开始演习以前,咱们经过下面命令来建立一个新的目录,接下来咱们全部的文件都将存放在这个目录当中。github
mkdir go-docker
在建立完成目录以后,咱们须要用go的原生命令来初始化相应的go模块。go mod是go11新增长的功能,在这以前出现很好几种模块化的解决方案,在go11以后官方终于给出了本身的解决方案.
cd go-docker
go mod init github.com/wenchangshou2/go-docker
咱们如今建立一个简单的hello world的服务,经过下面的命令来建立一个新的文件
touch hello_server.gogolang
如下是hello_server.go的内容docker
package main import ( "context" "fmt" "log" "net/http" "os" "os/signal" "syscall" "time" "github.com/gorilla/mux" "gopkg.in/natefinch/lumberjack.v2" ) func handler(w http.ResponseWriter, r *http.Request) { query := r.URL.Query() name := query.Get("name") if name == "" { name = "访客" } log.Printf("接收一个新的请求 %s\n", name) w.Write([]byte(fmt.Sprintf("Hello, %s\n", name))) } func main() { // 建立服务和root事件 r := mux.NewRouter() r.HandleFunc("/", handler) srv := &http.Server{ Handler: r, Addr: ":8080", ReadTimeout: 10 * time.Second, WriteTimeout: 10 * time.Second, } // 配置日志 LOG_FILE_LOCATION := os.Getenv("LOG_FILE_LOCATION") if LOG_FILE_LOCATION != "" { log.SetOutput(&lumberjack.Logger{ Filename: LOG_FILE_LOCATION, MaxSize: 500, // 总大小 MaxBackups: 3, MaxAge: 28, //最长保存时间 Compress: true, // 压缩数据,默认关闭 }) } // 启动服务 go func() { log.Println("启动服务") if err := srv.ListenAndServe(); err != nil { log.Fatal(err) } }() // 正常关闭 waitForShutdown(srv) } func waitForShutdown(srv *http.Server) { interruptChan := make(chan os.Signal, 1) signal.Notify(interruptChan, os.Interrupt, syscall.SIGINT, syscall.SIGTERM) // 阻塞,直到接收到信号 <-interruptChan ctx, cancel := context.WithTimeout(context.Background(), time.Second*10) defer cancel() srv.Shutdown(ctx) log.Println("结束退出") os.Exit(0) }
经过下面的命令来构建一个应用程序app
wcs@iMac ~/demo/go-docker go build ✔ >4950 23:22:27
go: finding github.com/gorilla/mux v1.7.4
go: downloading github.com/gorilla/mux v1.7.4
go: extracting github.com/gorilla/mux v1.7.4
go: downloading gopkg.in/natefinch/lumberjack.v2 v2.0.0
go: extracting gopkg.in/natefinch/lumberjack.v2 v2.0.0
经过go build,会自动下载须要的包,而且生成相应的可执行程序,经过下面的命令运行
wcs@iMac ~/demo/go-docker ./go-docker ✔
2020/02/26 23:24:05 启动服务curl
如今咱们尝试经过curl 来测试功能是否可用tcp
wcs@iMac ~ curl localhost:8080 ✔ 4958
Hello, 访客
wcs@iMac ~ curl http://localhost:8080\?name\=Rajeev
Hello, Rajeev模块化
如今咱们须要在上面的根目录下面新建一个文件,将文件的名称命名为Dockerfile.工具
# 构建该项目的基础镜像 FROM golang:latest # 添加做者信息 LABEL maintainer="wcs <wenchangshou@gmail.com>" # 设置当前的工做目录 WORKDIR /app # 复制 go mod和sum 文件 COPY go.mod go.sum ./ # 下载全部的依赖 RUN go mod download # 复制当前目录源文件到工做目录下面 COPY . . # 编译GO程序 RUN go build -o main . # 经过EXPOSE对外暴露服务的端口号 EXPOSE 8080 # 经过下面的命令来运行可执行文件 CMD ["./main"]
上面咱们已经定义了DockerFile,如今咱们须要经过命令将DockerFile生成构建和运行相应的映像
docker build -t go-docker .
你懂的国内应该各类缘由,访问会特别的慢,你能够利用aliyun和daocloud来进行回事。
经过下面的命令来查看镜像是否生成成功.
docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
go-docker lates 7654e880c09b 2 minutes ago 826MB
golang latest c3474fb0f20e 15 hours ago 809MB
如今咱们终端能够运行以前生成的镜像了
docker run -d -p 8888:8080 go-docker
3c80b9cb6b21a4fd50ea8b157ace4f3a9757e92b0e3cf9192639d30299d03d73查看运行的容器
你能够经过下面的命令来查看正在运行的容器
docker container ls
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
3c80b9cb6b21 go-docker "./main" 45 seconds ago Up 45 seconds 0.0.0.0:8888->8080/tcp quirky_shamir
如今咱们能够经过curl来测试应用是否正常
curl http://localhost:8888\?name\=wcs
Hello, wcs
如今运行完成以后咱们就能够中止相应的容器。在每次建立容器的时候都会产生一个惟一的id,那个id用来标识相应的容器,以后能够经过该id进行相应的操做
docker container stop 3c80b9cb6b21
如今咱们开始一个新的实例,在dockerfile里面提供了一个volume的字段,经过该字段能够定义容器内部的共享的目录。定义完成以后,你能够在建立容器的时候指定volume目录与本地目录的绑定。
在如下Dockerfile中,咱们声明一个volume路径/app/logs。容器会将日志文件写入/app/logs/app.log。运行docker映像时,咱们能够将宿主的目录挂载到该卷上。完成此操做后,咱们将可以从宿主的已挂载目录访问全部日志文件。
# 构建该项目的基础镜像 FROM golang:latest # 添加做者信息 LABEL maintainer="wcs <wenchangshou@gmail.com>" # 设置当前的工做目录 WORKDIR /app # 编译参数 ARG LOG_DIR=/app/logs # 建立log目录 RUN mkdir -p ${LOG_DIR} #环境变量 ENV LOG_FILE_LOCATION=${LOG_DIR}/app.log # 复制 go mod和sum 文件 COPY go.mod go.sum ./ # 下载全部的依赖 RUN go mod download # 复制当前目录源文件到工做目录下面 COPY . . # 编译GO程序 RUN go build -o main . # 经过EXPOSE对外暴露服务的端口号 EXPOSE 8080 # 声明Volumes VOLUME [${LOG_DIR}] # 经过下面的命令来运行可执行文件 CMD ["./main"]
咱们从新编译一个新的镜像
docker build -t go-docker-voluume -f Dockerfile.volume
如今咱们建立宿主将要绑定的目录
mkdir -p ~/logs/go-docker
docker run -d -p 8889:8080 -v ~/logs/go-docker:/app/logs go-docker-volume
394433aa0804dd24443d65090006e8becbe2aa26a883efe4324e79d4e085e261
如今咱们发现docker已经将日志写入到宿主对象的目录当中
tail -f ~/logs/go-docker/app.log
2020/02/26 16:01:38 启动服务
经过上面的示例,咱们生成了二个镜像,可是大家仔细查看,会发现即便这么小功能的一个程序,也会占用很大的存储空间。
docker image ls ✔ 4992 00:04:52
REPOSITORY TAG IMAGE ID CREATED SIZE
go-docker-volume latest 0b534f6bceaf 4 minutes ago 826MB
go-docker latest 7654e880c09b 19 minutes ago 826MB
golang latest c3474fb0f20e 16 hours ago 809MB
仅golang:latest镜像就占用了809MB,,而咱们生成程序也占用了826M的空间。
上面之因此这样大,是由于golang:latest里面包含了整个go的运行环境以及相应的编译环境,而咱们的应用程序仅在编译时须要这些,在编译完成以后仅运行就能够。根据这些需求咱们能够将原有的镜像拆分红多步,第一步是编译,以后咱们能够经过Alpine linux来进行最小化运行。
Dockerfile.multistage
FROM golang:latest as builder LABEL maintainer="wcs<wenchangshou@gmail.com>" WORKDIR /app COPY go.mod go.sum ./ RUN go mod download COPY . . RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o main . # 开始一个新的步骤 FROM alpine:latest RUN apk --no-cache add ca-certificates WORKDIR /root/ # 复制上一阶段的预构建二进制文件 COPY --from=builder /app/main . EXPOSE 8080 CMD ["./main"]
如今咱们生成新的镜像
docker build -t go-docker-optimized -f Dockerfile.multistage .
如今咱们再看压缩后的镜像
docker image ls REPOSITORY TAG IMAGE ID CREATED SIZE go-docker-optimized latest ed8e431271dc 23 seconds ago 14.1MB go-docker-volume latest 0b534f6bceaf 11 minutes ago 826MB go-docker latest 7654e880c09b 27 minutes ago 826MB golang latest c3474fb0f20e 16 hours ago 809MB alpine latest e7d92cdc71fe 5 weeks ago 5.59MB 如今你惊讶的发现压缩后的镜像仅有14.1M