什么是Docker以及如何使用Docker部署Go Web应用和Docker Compose模式

Docker介绍

物理机

在讲docker之前,先介绍一下物理机,发展到虚拟化时代的过程

物理机时代是计算机科学和信息技术的早期阶段,也是计算机系统的基础。在这个时代,计算机应用程序和操作系统都运行在物理硬件上,没有虚拟化或抽象层。每个计算任务都需要运行在单独的物理机上,资源分配和管理比较困难,也容易导致资源浪费。


虚拟机

为了克服物理机资源的浪费和管理难题,服务器虚拟化技术开始兴起。VMware 公司在2001年推出了第一个商用虚拟化产品,允许在单个物理机上运行多个虚拟机(VM)。每个虚拟机是一个独立的虚拟操作系统实例,它们共享物理机的资源。这种虚拟化技术极大地提高了资源利用率,并简化了管理。

我们现在在Windows操作系统上安装Linux操作系统都是通过 VMware Workstation 上通过虚拟机的镜像文件来安装虚拟机,这些虚拟机共用我们宿主机的硬件资源,我们就可以在一个宿主机上开启多个虚拟机部署多个app

但是虚拟化也是有局限性的,每一个虚拟机都是一个完整的操作系统,要分配系统资源,虚拟机多道一定程度时,操作系统本身资源也就消耗殆尽,或者说必须扩容。


容器化

为了解决应用部署时减少对硬件资源的浪费,容器化技术如Docker和Kubernetes兴起,它们提供了更轻量级的虚拟化方式,将应用程序和其依赖项打包成容器,以便在任何支持容器的环境中运行。容器与虚拟机相比更轻量级,它们共享主机操作系统的内核,只包含应用程序及其依赖项,因此更加节省资源。

Docker是实现容器化的一种方式,我们只需要使用相应的镜像文件,也就是将业务代码运行的环境,整体打包为单个的文件,就可以开启一个容器。下图是docker镜像分层结构

  1. 基础层(Base Layer) - Kernel

    • 基础层是 Docker 镜像的最底层,通常包含操作系统的内核(Kernel)。内核是操作系统的核心部分,负责管理硬件资源、进程管理和系统调用等底层功能。基础层一般就是我们的宿主机的内核,因此我们下载的docker镜像并不包括操作系统内核,因此文件比虚拟机镜像文件小,因为它只包括发行版,依赖和应用程序。
  2. 中间层(Intermediate Layers) - Debian 操作系统

    • 在基础层之上,可以有一个或多个中间层,代表了对基础层进行的修改和添加。对于 Debian 操作系统来说,这些中间层包含了 Debian 的用户空间组件,如文件系统、系统库、工具等。这些中间层可能包括 Debian 的软件包和配置,用于构建一个完整的 Debian 环境。它就是发行版,我们基于宿主机的Linux内核,就可以通过docker安装不同的发行版(ubuntu,centos...)来使用不同的Linux操作系统。
  3. 顶层(Top Layer) - Emacs 应用程序

    • 顶层是最终的镜像层,包含了您想要在容器中运行的 Emacs 应用程序。这个层包括了 Emacs 的二进制文件、配置文件、插件和用户数据。这是容器的实际文件系统,容器可以在其中修改并运行 Emacs。只有这一层是可以被修改的。

这样,整个 Docker 镜像分层结构就形成了。基础层提供了底层的操作系统内核,中间层添加了操作系统的用户空间组件(Debian),顶层包含了应用程序(Emacs)。每个层都是只读的,只有顶层是可写的,允许容器在其中运行和修改应用程序。

部署web应用

在本地开发了应用程序,它很可能有很多的依赖环境或包,甚至对依赖的具体版本都有严格的要求,当开发过程完成后,我们希望将应用程序部署到web服务器。这个时候必须确保所有依赖项都安装正确并且版本也完全相同,否则应用程序可能会崩溃并无法运行。如果想在另一个web服务器上也部署该应用程序,必须从头开始重复这个过程。这种场景就是Docker发挥作用的地方。

对于运行我们应用程序的主机,不管是笔记本电脑还是web服务器,我们唯一需要做的就是运行一个docker容器平台。从此以后,就不需要担心使用的是MacOS,Ubuntu还是其他。只需定义一次应用,即可随时随地运行。

我们要用docker部署web应用,就是要自己实现每一层的定制,然后构建成一个镜像文件,通过这个我们自己构建的镜像文件,就可以把我们的web应用部署到容器中。而定制每一层,就是通过写Dockerfile文件来实现。

Dockerfile

Dockerfile 是用于定义 Docker 镜像构建过程的文本文件。它包含了一系列命令,用于描述如何构建镜像。以下是 Dockerfile 中常用的一些命令:

  1. FROM:指定基础镜像。它是 Dockerfile 的第一个命令,用于指定构建新镜像的起点。例如:

    FROM ubuntu:20.04

  2. MAINTAINER:指定镜像的维护者信息。虽然不是必需的,但可以提供有关镜像的相关信息。

  3. RUN:在容器内执行命令。它可以用于在构建过程中运行各种命令,例如安装软件包、配置环境等。例如:

    RUN apt-get update && apt-get install -y nginx

  4. COPYADD :用于将文件从主机复制到容器内。COPY 用于简单地复制文件,而 ADD 具有一些额外的功能,如自动解压缩归档文件。例如:

    COPY ./app /app

  5. WORKDIR:设置容器内的工作目录。这可以用于指定容器中命令的执行路径。例如:

    WORKDIR /app

  6. EXPOSE:声明容器监听的端口。它不会实际打开端口,但可以在运行容器时提供端口映射。例如:

    EXPOSE 80

  7. CMDENTRYPOINT :定义容器启动时执行的命令。CMD 定义了容器的默认命令,可以在运行容器时被覆盖。ENTRYPOINT 也定义了容器的启动命令,但不会被覆盖,除非在运行容器时使用 --entrypoint 参数。例如:

    CMD ["nginx", "-g", "daemon off;"]

  8. ENV:设置环境变量。可以用于在容器内定义环境变量,以便应用程序在运行时使用。例如:

    ENV APP_VERSION=1.0

这些是 Dockerfile 中的一些常用命令。Dockerfile 还支持其他命令和更高级的功能,可以根据具体需求进行使用。通过组合这些命令,您可以定义 Docker 镜像的构建过程,使其能够在不同的环境中正确运行您的应用程序。

简单代码demo

通过一个简单的web服务看一下它是怎么通过docker部署的

go 复制代码
package main

import (
	"fmt"
	"net/http"
)

func main() {
	http.HandleFunc("/", hello)
	server := &http.Server{
		Addr: ":8888",
	}
  fmt.Println("server startup...")
	if err := server.ListenAndServe(); err != nil {
		fmt.Printf("server startup failed, err:%v\n", err)
	}
}

func hello(w http.ResponseWriter, _ *http.Request) {
	w.Write([]byte("hello world"))
}

编写Dockerfile

在demo的同目录下,新建一个Dockerfile,并加入以下内容

ini 复制代码
# 基础镜像golang:alpine,这个镜像运行的是alpine Linux发行版,该发行版的大小很小并且内置了Go
FROM golang:alpine

# 为我们的镜像设置必要的环境变量
ENV GO111MODULE=on \
    GOPROXY=https://goproxy.cn,direct \
    CGO_ENABLED=0 \
    GOOS=linux \
    GOARCH=amd64

# 移动到工作目录:/build
WORKDIR /build

# 将代码复制到容器中
COPY . .

# 将我们的代码编译成二进制可执行文件app
RUN go build -o app .

# 移动到用于存放生成的二进制文件的 /dist 目录
WORKDIR /dist

# 将二进制文件从 /build 目录复制到这里
RUN cp /build/app .

# 声明服务端口
EXPOSE 8888

# 启动容器时运行的命令
CMD ["/dist/app"]

构建镜像

在项目目录下,执行下面的命令创建镜像,并指定镜像名称为xx_goweb_app:

docker build . -t xx_goweb_app

等待构建过程结束,输出如下提示:

erlang 复制代码
...
Successfully built 90d9283286b7
Successfully tagged xx_goweb_app:latest

现在我们已经准备好了镜像,但是目前它什么也没做。我们接下来要做的是运行我们的镜像,以便它能够处理我们的请求。运行中的镜像称为容器。

执行下面的命令来运行镜像:

arduino 复制代码
docker run -p 8888:8888 xx_goweb_app

标志位-p用来定义端口绑定。由于容器中的应用程序在端口8888上运行,我们将其绑定到主机端口也是8888。如果要绑定到另一个端口,则可以使用-p $HOST_PORT:8888。例如-p 5000:8888

现在就可以测试下我们的web程序是否工作正常,打开浏览器输入http://127.0.0.1:8888就能看到我们事先定义的响应内容如下:

hello world

Docker Compose模式

Docker Compose 是一个用于定义和运行多容器 Docker 应用程序的工具。它允许您使用一个单独的文件(称为 docker-compose.yml)来配置和启动多个 Docker 容器,这些容器可以协同工作以构建完整的应用程序。我在windows安装了Docker Desktop,Docker Compose 已经预安装在Docker Desktop中,所以不需要额外安装,除此情况需要安装Docker Compose,方法自行google

类似我们写一个商品微服务,它不仅有web服务,还需要使用到mysql和consul注册中心,这个时候我们就需要用到两个容器来协同工作部署这个微服务,通过 Docker Compose 部署包含微服务和 mysql的应用程序是一种常见的做法。

首先在项目目录下写好dockerfile文件并构建打包成镜像,并且把consul和mysql镜像也下载好

然后编写docker-compose.yaml文件

yaml 复制代码
version: '3'
services:
  go-service:
#    build:
#      context: .
#      dockerfile: Dockerfile
    image: goods_service2
#    ports:
#      - 8382:8382
    volumes:
      - ./conf:/conf
    restart: always
    depends_on:
      - mysql
      - consul 
    networks:
      - my-network   

  mysql:
    image: mysql:latest
    restart: always
    environment:
      - MYSQL_ROOT_PASSWORD=123456
      - MYSQL_DATABASE=goods_service
      - MYSQL_USER=fengmi
      - MYSQL_PASSWORD=123456
    networks:
      - my-network

  consul:
    image: consul:1.10.3
    restart: always
    ports:
      - 8500:8500
    networks:
      - my-network
networks:
  my-network:

这里我们用文件映射./conf:/conf ,这样就可以在宿主机的配置文件修改容器内的配置文件,mysql下的environment可以乱填,不需要你去mysql容器中创建数据库用户名密码啥的,他自己帮你完成好了。并且配置一个network,这样可以让三个容器共享一个网络。

终端运行

docker-compose up -d

这样容器就部署成功了

相关推荐
paopaokaka_luck3 小时前
【360】基于springboot的志愿服务管理系统
java·spring boot·后端·spring·毕业设计
码农小旋风5 小时前
详解K8S--声明式API
后端
Peter_chq5 小时前
【操作系统】基于环形队列的生产消费模型
linux·c语言·开发语言·c++·后端
Yaml45 小时前
Spring Boot 与 Vue 共筑二手书籍交易卓越平台
java·spring boot·后端·mysql·spring·vue·二手书籍
小小小妮子~5 小时前
Spring Boot详解:从入门到精通
java·spring boot·后端
hong1616885 小时前
Spring Boot中实现多数据源连接和切换的方案
java·spring boot·后端
追风林6 小时前
mac 本地docker-mysql主从复制部署
mysql·macos·docker
睡觉谁叫~~~6 小时前
一文解秘Rust如何与Java互操作
java·开发语言·后端·rust
城南vision8 小时前
Docker学习—Docker核心概念总结
java·学习·docker
wclass-zhengge8 小时前
Docker篇(Docker Compose)
运维·docker·容器