首先我们要知道两种文件:Dockerfile和docker-compose.yml,在一些开源项目中很常见。
Docker安装
- 详见官方安装文档:Install Docker Engine
- 或者菜鸟教程: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的教程:
- 我们先要拉取一个基础运行的镜像,这里就用官方镜像库中的
golang:lastest来作为基础镜像(也可以指定版本),方便后续操作,我们将他别名为builder。
FROM golang:lastest AS builder在这个基础镜像环境下,相当于我们的程序已经在有Go语言环境下的虚拟机下运行了。
- 我们要配置好该有的一些设置。在Go语言写的后端程序中,有一些包下载的时候因为众所周知网络原因 (我想学习Go语言的同学都有感受),一些包没法直接拉取下来,需要借助设置
GOPROXY,借助代理来下载包。同时保证我们编译的程序是Linux系统、X86架构环境运行 (使用arm架构的同学自行修改),所以我们需要设置程序的环境变量。
ENV GO111MODULE=on \
GOPROXY=https://goproxy.cn,direct \
CGO_ENABLED=0 \
GOOS=linux \
GOARCH=amd64- 我们设置一个工作目录
/build(叫啥都行,规范取名),让程序在这个目录中编译和执行。
WORKDIR /build为什么要设置工作目录:WORKDIR 指令用于设置 Docker 容器中的工作目录。它会在容器中创建一个指定的目录,并将其作为后续指令(如 RUN, CMD, ENTRYPOINT)的默认工作目录。也就是说,当我们执行其他指令时,会在这个工作目录下进行。
- 将我们的代码复制到容器中。
COPY . .为什么COPY指令两边都是.:使用 COPY 指令时,第一个参数表示要复制的源文件或目录的路径,第二个参数表示目标路径。当第二个参数是一个相对路径时,它将被视为相对于 WORKDIR 指令设置的工作目录。
设置工作目录后,COPY . .中第一个参数.表示当前目录,也就是 Dockerfile 所在的目录,第二个参数.表示相对于工作目录 /build 的当前目录,也就是将当前目录复制到容器的 /build 目录中。
- 我们就要开始下载程序中用到的第三方包了,这里需要执行命令
go mod download。
RUN go mod download- 我们将程序编译成二进制可执行文件,执行命令
go build -o main。
RUN go build -o main- 为了节省运行时的性能开销,因为我们已经编译好了可运行的二进制文件,所以运行起来并不需要Go语言环境,这里可以选择在更小的基础镜像中运行,这里我们选择
debian:bullseye-slim,只包含了最基本的系统组件和软件包。
FROM debian:bullseye-slim- 在运行起来的时候可能会发现,当程序有对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- 然后从原来的编译虚拟机中将编译好的可执行文件以及配置文件(示例为
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服务配置来支撑。
- 打上版本号和services服务。
# 版本号
version: '3'
# 各种服务
services:
# ...写下面的服务- 配置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
- 配置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_DATABASE、MYSQL_USER和MYSQL_"password"作为初始化数据库的配置,更多查看:https://hub.docker.com/_/mysql
- 配置自己写好的后端服务,这里和上面两个不一样的是,上面是使用的
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命令
- 上面的运行过程之后,需要保证shell界面不关闭,否则程序将会终止,所以我们需要他在后台运行。
在启动时,加一个参数 -d 就能让他在后台运行:
$ sudo docker-compose up -d- 如果想要看到后台运行服务的日志,则
logs命令就能看到其日志:
$ sudo docker-compose logs- 若要关闭服务,则
down命令就能关闭:
$ sudo docker-compose down- 当修改过程序或者Dockerfile,则启动时需要重新构建,加上参数
--build就能重新构建:
$ sudo docker-compose up --build更多命令可以查看:docker-compose命令
以上就是所有要讲的教程啦,希望能对你有所帮助!如果有错误或者有疑问,欢迎在评论区指出~