首先我们要知道两种文件:Dockerfile和docker-compose.yml,在一些开源项目中很常见。

Docker安装

Dockerfile

Dockerfile 是用于构建 Docker 镜像的一种脚本语言,它包含一系列的指令和参数,用于指定如何构建镜像。在 Dockerfile 中,我们可以指定基础镜像、容器中运行的命令、需要复制到容器中的文件、环境变量等等。通过编写 Dockerfile,我们可以将应用程序打包到一个独立的、可移植的镜像中,这使得应用程序的部署和运行变得非常简单和可靠。

上面的文本通俗来说,Dockerfile 是一个用于构建 Docker 镜像的脚本语言,它包含一系列的指令和参数,用于指定如何构建镜像并完成应用程序的打包和部署。

下面是一个示例,将一个简单的Go语言程序用Docker运行起来:

# 拉取运行的镜像
FROM golang:latest
# 设置工作目录
WORKDIR /app
# 拷贝文件
COPY . .
# 构建项目,编译成可执行的二进制文件
RUN go build -o main .
# 暴露端口
EXPOSE 8080
# 运行构建项目的二进制文件
CMD ["./main"]

Docker Compose

🌟 使用之前需要安装docker-compose:Installation scenarios,这里推荐在安装了Docker之后,在 Ubuntu 等类 Unix 系统上使用 sudo apt install docker-compose 安装,CentOS 等类 Red Hat 的系统上使用 sudo yum install docker-compose 安装。

Docker Compose 是一个 Docker 官方提供的工具,用于简化多个 Docker 容器的管理。通过 Docker Compose,我们可以使用一个 YAML 文件来定义和配置多个容器,然后使用一个命令就可以启动、停止和管理这些容器。使用 Docker Compose 可以帮助我们更方便地管理多个容器,同时也可以加快应用程序的开发和部署流程。

简言之就是可以将多个 Docker 容器组合在一起,并通过一个配置文件来定义它们之间的关系和运行方式

下面是一个示例:

version: '3'
services:
  web:
    build: .
    ports:
      - "8080:8080"
    depends_on:
      - db
  db:
    image: mysql:5.7
    environment:
      MYSQL_ROOT_"password": root
      MYSQL_DATABASE: test

这个示例使用了Docker Compose的YAML文件格式,定义了两个服务:web和db,在该示例中,web服务是一个基于Dockerfile构建的Web应用程序,该应用程序监听8080端口。db服务则是一个使用MySQL 5.7镜像启动的数据库服务。

Docker compose中可以使用服务名称来指定服务之间的连接,在这个示例中,web服务可以使用 db 作为数据库的主机名来连接 db 服务

部署后端程序

一般来说,一个后端程序需要有程序主体配置文件关系型数据库Redis数据库,这里关系型数据库用MySQL作为示例部署一个Go语言的后端程序

确定部署流程

我们部署一个后端程序一般的步骤是:

  • 编写配置文件,方便使用时随时修改
  • 有Golang环境,将程序编译成可执行二进制文件
  • 运行程序
  • 程序连接数据库
  • 用户调用接口使用

那么我们就可以按照上面步骤来编写Dockerfile和docker-compose.yml文件了

编写Dockerfile文件

Dockerfile中常用的指令解释,还是很好理解的:

  • FROM: 指定基础镜像,例如 FROM golang:1.16-alpine,表示以 golang:1.16-alpine 为基础镜像构建 (这里的基础镜像来自 Docker 镜像仓库)
  • RUN: 在容器内部执行命令,例如 RUN apt-get update && apt-get install -y curl,表示在容器内部执行更新和安装 curl 命令。
  • COPY: 复制本地文件到容器内部,例如 COPY ./app /app,表示将主机当前目录下的 app 目录复制到容器内部的 /app 目录。
  • WORKDIR: 设置容器内部的工作目录,例如 WORKDIR /app,表示将容器内部的工作目录设置为 /app。
  • EXPOSE: 暴露容器内部的端口,例如 EXPOSE 8080,表示暴露容器内部的 8080 端口。
  • CMD: 设置容器启动时需要执行的命令,例如 CMD ["./app"],表示容器启动时执行 /app 命令。
  • ENTRYPOINT: 用于设置容器启动时需要执行的程序。
  • ENV: 用于设置环境变量。

下面就是编写Dockerfile的教程:

  1. 我们先要拉取一个基础运行的镜像,这里就用官方镜像库中的 golang:lastest 来作为基础镜像(也可以指定版本),方便后续操作,我们将他别名为 builder
FROM golang:lastest AS builder

在这个基础镜像环境下,相当于我们的程序已经在有Go语言环境下的虚拟机下运行了。

  1. 我们要配置好该有的一些设置。在Go语言写的后端程序中,有一些包下载的时候因为众所周知网络原因 (我想学习Go语言的同学都有感受),一些包没法直接拉取下来,需要借助设置 GOPROXY ,借助代理来下载包。同时保证我们编译的程序是 Linux 系统、 X86 架构环境运行 (使用arm架构的同学自行修改),所以我们需要设置程序的环境变量。
ENV GO111MODULE=on \
    GOPROXY=https://goproxy.cn,direct \
    CGO_ENABLED=0 \
    GOOS=linux \
    GOARCH=amd64
  1. 我们设置一个工作目录 /build(叫啥都行,规范取名),让程序在这个目录中编译和执行。
WORKDIR /build
为什么要设置工作目录:WORKDIR 指令用于设置 Docker 容器中的工作目录。它会在容器中创建一个指定的目录,并将其作为后续指令(如 RUN, CMD, ENTRYPOINT)的默认工作目录。也就是说,当我们执行其他指令时,会在这个工作目录下进行。
  1. 将我们的代码复制到容器中。
COPY . .
为什么COPY指令两边都是.使用 COPY 指令时,第一个参数表示要复制的源文件或目录的路径,第二个参数表示目标路径。当第二个参数是一个相对路径时,它将被视为相对于 WORKDIR 指令设置的工作目录。
设置工作目录后,COPY . . 中第一个参数 . 表示当前目录,也就是 Dockerfile 所在的目录,第二个参数 . 表示相对于工作目录 /build 的当前目录,也就是将当前目录复制到容器的 /build 目录中。
  1. 我们就要开始下载程序中用到的第三方包了,这里需要执行命令 go mod download
RUN go mod download
  1. 我们将程序编译成二进制可执行文件,执行命令 go build -o main
RUN go build -o main
  1. 为了节省运行时的性能开销,因为我们已经编译好了可运行的二进制文件,所以运行起来并不需要Go语言环境,这里可以选择在更小的基础镜像中运行,这里我们选择 debian:bullseye-slim ,只包含了最基本的系统组件软件包
FROM debian:bullseye-slim
  1. 在运行起来的时候可能会发现,当程序有对https协议的API接口等请求时会出现 "docker tls: failed to verify certificate: x509: certificate signed by unknown authority" 这表明我们运行的环境里面没有配置证书,这时我们可以通过apt包管理来安装相关包。
RUN apt update && apt install apt-utils && apt install -y ca-certificates

❗ 在运行起来的时候还可能会发现,时区不太对,所以还要安装响应的包和指令来更改时区。

RUN apt-get install -y tzdata &&\
ln -fs /usr/share/zoneinfo/Asia/Shanghai /etc/localtime &&\
dpkg-reconfigure -f noninteractive tzdata
如果有其他的需求也可自己装一些包。

❗ 如果是国内服务器换源会更快,可以在上面指令之前加上换源指令。

RUN echo "deb http://mirrors.aliyun.com/debian bullseye main" > /etc/apt/sources.list
RUN echo "deb http://mirrors.aliyun.com/debian-security bullseye-security main" >> /etc/apt/sources.list
RUN echo "deb http://mirrors.aliyun.com/debian bullseye-updates main" >> /etc/apt/sources.list
  1. 然后从原来的编译虚拟机中将编译好的可执行文件以及配置文件(示例为 config.yaml)复制到这个新的镜像环境中。
COPY --from=builder /build/main /
COPY ./config.yaml /
下面为啥不用 --from=builder:因为第一个是在上面镜像构建的环境中的build工作目录中编译好的文件,不加是直接从当前目录(没在容器中)复制文件。当然也可以加,从上面镜像构建的环境中获取,两个文件一样,所以没必要加。

这里编写Dockerfile文件就到尾声了,下面是上面步骤的完整 Dockerfile 文件内容。

因为每个指令都会产生一个新的镜像层,不精简指令的话会产生很多镜像,所以下面代码为精简后的代码。
# 构建的基础镜像
FROM golang:latest AS builder

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

# 设置工作目录
WORKDIR /build

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

# 下载依赖项和编译成二进制可执行文件
RUN go mod download &&\
    go build -o main

# 使用一个更小的镜像运行
FROM debian:bullseye-slim

# 从builder环境COPY编译的二进制文件
COPY --from=builder /build/main /

# COPY配置文件
COPY ./config.yaml /

# 换源 & 安装证书 & 调整时区 & 增加执行权限
RUN echo "deb http://mirrors.aliyun.com/debian bullseye main" > /etc/apt/sources.list &&\
    echo "deb http://mirrors.aliyun.com/debian-security bullseye-security main" >> /etc/apt/sources.list &&\
    echo "deb http://mirrors.aliyun.com/debian bullseye-updates main" >> /etc/apt/sources.list &&\
    apt update && \
    apt install apt-utils &&\
    apt install -y ca-certificates &&\
    apt-get install -y tzdata &&\
    ln -fs /usr/share/zoneinfo/Asia/Shanghai /etc/localtime &&\
    dpkg-reconfigure -f noninteractive tzdata &&\
    chmod +x wait-for-it.sh &&\
    chmod +x ./main
这里我们编写的Dockerfile文件是给到下面docker-compose.yml使用的,所以不需要将编译好的程序在这里使用执行指令执行。

编写docker-compose.yml文件

Docker compose文件使用的是.yml后缀的YAML格式配置文件,文件内主要包括:

  • version: 指定 Docker Compose 文件的版本。该字段值需要使用字符串格式进行指定,例如 version: '3',目前最新版本使用'3'版本就好
  • services: 多个容器的服务配置都在services下:

    • container_name:字段用于指定容器的名称。每个容器都有自己的名称,方便来标识和管理容器
    • image 或 build:指定服务使用的镜像名称或 Dockerfile 路径。如果同时指定了 image 和 build,则 Compose 将使用 build 选项构建镜像,并使用 image 选项指定的名称来标记该镜像。
    • command:指定容器启动时运行的命令。
    • environment:指定容器运行时需要的环境变量。
    • ports:指定主机端口与容器端口的映射关系。格式为 host_port:container_port。
    • depends_on:指定容器间的依赖关系。这可以确保某个服务在其所依赖的服务启动之后再启动。
    • volumes:用于定义容器和主机之间的数据卷(volume)映射关系。数据卷是一种持久化存储数据的方式,它可以在容器和主机之间共享数据
    • networks:用于定义容器所属的网络。默认情况下,Docker Compose 会为每个项目创建一个默认网络,并将所有服务连接到该网络中。
    • restart:用于指定容器退出时的重启策略。默认情况下,Docker Compose 不会自动重启容器。
    • healthcheck:用于定义容器的健康检查配置。健康检查用于检测容器是否正常运行,如果检测失败,则 Docker 会尝试重启容器。

认识这些后,我们就可以编写docker-compose.yml文件了,再确定一下我们需要的服务:后端程序、关系型数据库和Redis缓存数据库,所以我们需要编写3个services服务配置来支撑。

  1. 打上版本号和services服务。
# 版本号
version: '3'

# 各种服务
services:
# ...写下面的服务
  1. 配置Redis服务,这里我们使用使用redis作为服务名,redis:latest镜像作为redis数据库服务,project_redis作为运行时容器名称,将容器内的6379端口映射到本机运行环境上(因为默认端口为6379),以及使用REDIS_"password"这个镜像规定的环境变量来设置Redis数据库的密码。
# redis服务名
redis:
    # 使用的镜像
    image: redis:latest
    # 容器名
    container_name: project_redis
    # 端口映射
    ports:
        - "6379:6379"
    # 环境变量配置
    environment:
        # Redis服务器密码
        REDIS_"password": redis_"password"

ports参数的端口映射的形式为:\<本机端口\>:\<容器内端口\>

environment中,还有更多的参数,这里不常用就没有列举出来,具体看:https://hub.docker.com/_/redis

  1. 配置MySQL服务,这里我们使用使用 mysql 作为服务名,mysql:latest 镜像作为mysql数据库服务。
mysql:
    image: mysql:latest
    container_name: project_mysql
    ports:
        - "3306:3306"
    # 映射存储数据
    volumes:
        - ./mysql/data:/var/lib/mysql
    environment:
        # mysql中ROOT密码
        MYSQL_ROOT_"password": "password"
        # 数据库名
        MYSQL_DATABASE: db
        # 数据库普通用户名
        MYSQL_USER: user
        # 数据库普通用户密码
        MYSQL_"password": user_"password"

我们使用 volumes 关键字来将容器内的数据映射到本地目录 (\<本地目录\>:\<容器内目录\>),防止容器被删除后在容器内的数据取不出来,不会轻易丢失数据。这份数据还能作为本机运行的mysql使用。

environment中,必须的有MYSQL_ROOT_"password"MYSQL_DATABASEMYSQL_USERMYSQL_"password"作为初始化数据库的配置,更多查看:https://hub.docker.com/_/mysql

  1. 配置自己写好的后端服务,这里和上面两个不一样的是,上面是使用的 image 关键字构建项目,而这里我们使用 build 来构建项目,volumes 将配置文件放在本地映射到容器内,方便我们及时修改配置文件不用重新构建项目command 作为我们此服务最终执行的命令,environment 可以放一些环境变量供我们的程序调用。
api_service:
    container_name: project_api
    # build构建自己的项目
    build:
        # 项目路径
        context: .
        # Dockerfile文件名
        dockerfile: Dockerfile
    # 端口映射
    restart: always
    ports:
        - "3000:3000"
    # 暴露配置文件到本机
    volumes:
        - ./config.yaml:/config.yaml
    # 执行命令等待
    command: sh -c "./wait-for-it.sh mysql:3306 -s -t 60 && ./wait-for-it.sh redis:6379 -s -t 60 -- ./main"
    environment:
        MYSQL_HOST: mysql
        MYSQL_PORT: 3306
        MYSQL_DATABASE: text2solution
        MYSQL_USER: user
        MYSQL_"password": user_"password"
        REDIS_HOST: redis
        REDIS_PORT: 6379
    # 健康检查
    healthcheck:
        # 检查命令(不同程序不一样)
        test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
        # 检查间隔
        interval: 30s
        # 超时时间
        timeout: 10s
        # 重试次数
        retries: 3
        # 启动延迟
        start_period: 40s
    # 依赖
    depends_on:
        # 依赖redis和mysql
        mysql:
            # 依赖mysql服务健康时候才会启动(除此之外,还有none、service_started、service_healthy)
            condition: service_healthy
        redis:
            condition: service_healthy

build 关键字下的 context 表示构建上下文路径,即Dockerfile所在的路径,我们的Dockerfile就在当前目录下,和docker-compose.yml文件在同一个目录下,那么就指定为当前目录;Dockerfile 表示Dockerfile文件的名称。

depends_on 表示我们这个服务要依赖那些服务,这里我们填写上面的mysql以及redis服务。填写依赖后,我们就可以通过使用其服务名作为主机名来连接其服务。比如连接mysql的host我们直接填写mysql就行,程序执行时会自动解析成mysql服务的主机IP地址。

至此,搭建后端程序的docker-compose.yml的示例已经完成,下面是完整代码:

# 版本号
version: '3'

# 各种服务
services:
    # redis服务
    redis:
        # 使用的镜像
        image: redis:latest
        # 容器名
        container_name: project_redis
        # 端口映射
        ports:
            - "6379:6379"
        # 健康检查
        healthcheck:
            test: ["CMD", "redis-cli", "ping"]
            interval: 30s
            timeout: 10s
            retries: 3
            start_period: 40s
        # 环境变量配置
        environment:
            # Redis服务器密码
            REDIS_"password": redis_"password"# ...写下面的服务

    # mysql服务
    mysql:
        image: mysql:latest
        container_name: project_mysql
        ports:
            - "3306:3306"
        # 映射存储数据
        volumes:
            - ./mysql/data:/var/lib/mysql
        # 健康检查
        healthcheck:
            test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
            interval: 30s
            timeout: 10s
            retries: 3
            start_period: 40s
        environment:
            # mysql中ROOT密码
            MYSQL_ROOT_"password": "password"
            # 数据库名
            MYSQL_DATABASE: db
            # 数据库普通用户名
            MYSQL_USER: user
            # 数据库普通用户密码
            MYSQL_"password": user_"password"
    
    api_service:
        container_name: project_api
        # build构建自己的项目
        build:
            # 项目路径
            context: .
            # Dockerfile文件名
            dockerfile: Dockerfile
        # 端口映射
        restart: always
        ports:
            - "3000:3000"
        # 暴露配置文件到本机
        volumes:
            - ./config.yaml:/config.yaml
        # 执行命令等待
        command: sh -c "./main"
        environment:
            MYSQL_HOST: mysql
            MYSQL_PORT: 3306
            MYSQL_DATABASE: text2solution
            MYSQL_USER: user
            MYSQL_"password": user_"password"
            REDIS_HOST: redis
            REDIS_PORT: 6379
        # 健康检查
        healthcheck:
            # 检查命令(不同程序不一样)
            test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
            # 检查间隔
            interval: 30s
            # 超时时间
            timeout: 10s
            # 重试次数
            retries: 3
            # 启动延迟
            start_period: 40s
        # 依赖
        depends_on:
            # 依赖redis和mysql
            mysql:
                # 依赖mysql服务健康时候才会启动(除此之外,还有none、service_started、service_healthy)
                condition: service_healthy
            redis:
                condition: service_healthy
上面只是一个示例程序的部署编写过程,如果需要更复杂的功能请自行添加。

执行程序

我们这里需要用到docker-compose指令,首先打开我们的项目根目录,将Dockerfile文件和docker-compose.yml文件放在项目根目录中,输入指令:

$ sudo docker-compose up

服务就在按照我们编写的docker-compose.yml中的服务按照顺序进行构建,在对于本地没有下载的镜像也会进行下载。

如果mysql以及redis服务已经完成之后,接下来会进入到我们自己程序的构建中。在全部服务构建完成后,就会开始运行我们的服务。

常用的docker-compose命令

  1. 上面的运行过程之后,需要保证shell界面不关闭,否则程序将会终止,所以我们需要他在后台运行。

在启动时,加一个参数 -d 就能让他在后台运行:

$ sudo docker-compose up -d
  1. 如果想要看到后台运行服务的日志,则 logs 命令就能看到其日志:
$ sudo docker-compose logs
  1. 若要关闭服务,则 down 命令就能关闭:
$ sudo docker-compose down
  1. 当修改过程序或者Dockerfile,则启动时需要重新构建,加上参数 --build 就能重新构建:
$ sudo docker-compose up --build
更多命令可以查看:docker-compose命令

以上就是所有要讲的教程啦,希望能对你有所帮助!如果有错误或者有疑问,欢迎在评论区指出~

最后修改:2024 年 07 月 19 日
如果觉得我的文章对你有用,请随意赞赏