Docker 从零开始部署后端项目和 MySQL 数据库

本贴最后更新于 436 天前,其中的信息可能已经时过境迁

预备知识

  • Docker 是什么
  • Docker 镜像是什么
  • Docker 容器是什么
  • Docker Compose 是什么
  • Docker Compose 中的 Service 是什么

请自行上网查阅以上内容相关资料,否则无法阅读下文

安装 Docker

Windows

去官网 Docker: Accelerated Container Application Development 下载并安装。安装完成之后会提示重启。通常情况下正常重启就行。但我一开始试的那台电脑装了 Windows 和黑苹果双系统,在开机的时候会提示选择那个系统。结果试了好几次电脑都没法正常启动,我甚至一度以为是电脑坏了。最后进入 BIOS 把首选的启动选项从双系统选择器改为 Windows 自带的启动器,这样在重启的时候会直接进入 Windows,终于成功。

docker 运行的底层系统需要是 Linux,Windows10 之后的版本支持在 Windows 内运行 Linux,也就是所谓的 wsl。但大部分的系统本身的 wsl 版本太老,当你打开 docker 的时候也会出现提示。因此在第一次启动 docker 时,需要在终端执行:

wsl --update

更新 wsl 才行。

docker 默认的镜像存储空间是在 C 盘,之后随着使用过程中镜像数量的增多,占据的空间会越来越大。C 盘空间紧张的同学可以手动修改镜像存储空间。

点击右上角的齿轮-Resources-Advance 中就可以修改镜像的默认存储位置。

image.png

Mac

同样去官网下载并安装即可,不需要执行 wsl --update 过程。

部署 MySQL

为了简单起见,先尝试部署一个 MySQL 容器。MySQL 已经提供了官方的镜像,直接根据该镜像创建容器即可。

执行以下命令即可部署一个最基本的 MySQL 容器:

docker run -p 3306:3306 --name mysql -e MYSQL_ROOT_PASSWORD=123456 -d --restart always mysql:5.7
  • -p:端口映射。:左边的是宿主机(本机)端口,右边的是容器内端口。3306:3306 就是将容器内的 3306 端口映射到宿主机的 3306 端口。如果本机已经部署了在本地运行的 MySQL 服务(默认的端口就是 3306),那么就会冲突。这时候可以改成 3406:3306 或者任意空闲的本机端口。
  • --name:这是给运行的容器起的名字,用于之后重启/停止/删除容器用
  • -e:这是容器内的一些环境变量,这里设定了 root 用户的密码是 123456
  • -d:代表在后台运行容器
  • --restart:代表重启 Docker 服务后该容器是否重启。always 代表每次重启 Docker 服务后该容器也会重启。这样,只要设定 Docker 服务开机自启,那么这个 MySQL 容器也会开机启动,就像部署在本地的 MySQL 服务一样
  • mysql:5.7:这是容器使用的镜像名称和标签,标签通常使用版本号来填写。mysql:5.7 就代表 5.7 版本的 MySQL 镜像

启动后,尝试使用 Navicat 等数据库管理工具连接,用户名 root,密码 123456,就可以成功连接到 Docker 内部署的 MySQL 容器,之后的使用方法和本地部署的 MySQL 无异。

查看 Docker 容器

执行 docker ps 命令可以查看正在运行的 Docker 容器,行 docker ps -a 命令可以查看所有 Docker 容器,显示的信息类似下面这样

image.png

如果想要停止一个容器,可以执行 docker stop 容器ID或容器名。容器 ID 就是图中的 CONTAINER ID,容器名就是图中的 NAMES

如果想要删除一个容器,可以执行 docker rm 容器ID或容器名

查看 Docker 镜像

执行 docker images 命令可以查看本地已存在的镜像。如图

截屏 2023082617.38.19.png

如果想要删除一个镜像,首先需要确保该镜像没有被任何容器使用,即便是已经停止的容器也不行。确认完毕之后可以执行 docker rmi 镜像ID或镜像名:镜像标签 来删除。实际操作中,镜像 ID 可以不填完整,只要填前几个能够和其他镜像区分开来的字符即可。

部署 Django 后端应用

为了部署 Django 应用,我们需要首先打包出自己的镜像,之后再使用该镜像创建容器。

在 Django 项目的工作目录下,创建名为 Dockerfile 的文件,其中的内容为:

# 使用官方的 Python 镜像作为基础镜像
FROM python:3.10.4

# 设置工作目录
WORKDIR /app
# 设置 pip 使用清华源
ENV PIP_INDEX_URL=https://pypi.tuna.tsinghua.edu.cn/simple
# 将当前目录下的所有文件复制到工作目录
COPY . /app/

# 安装项目所需的依赖
RUN pip install -r requirements.txt

# 将容器内的端口暴露出来,这里使用 Django 默认的端口 8000
EXPOSE 8000

# 启动 Django 项目
CMD ["python", "manage.py", "runserver", "0.0.0.0:8000"]

之后,在同个目录下执行:

docker build -t my-django-app .

这样就可以打包出名为 my-django-app 的镜像。最后的 . 代表读取当前目录下的 Dockerfile 文件来构建镜像

之后再运行

docker run -p 8000:8000 --name django-app my-django-app

我这里没有写-d,就是为了可以看到 Django 的启动过程

使用 docker compose 同时部署后端应用和 MySQL 数据库,并使两者能够连接

当一个应用包含多个容器时,就需要使用 Docker compose 来进行组织和管理。我们安装 Docker 时,通常情况下默认也安装了 Docker compose。

首先我们为了确保 Django 应用能够连上 MySQL,需要去 settings.py 文件中修改数据库连接配置,参考以下内容:

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',  # 默认
        'NAME': 'mydb',  # 连接的数据库名
        'HOST': 'database', # mysql的地址
        'PORT': 3306,  # mysql的端口
        'USER': 'root',  # mysql的用户名
        'PASSWORD': '123456'  # mysql的密码
    }
}

重点就在 HOST 和 NAME,需要和之后创建的 docker-compose.yml 中一致。HOST 对应 service 的名称,NAME 对应 MYSQL_DATABASE 这个环境变量。

之后创建一个 docker-compose.yml 文件,可以在任意目录下,但推荐和 Dockerfile 在同个目录下。

文件内容如下:

version: '3'
services:
  database: 
    image: mysql:5.7
    container_name: mysql
    ports:
      - "3306:3306"
    volumes:
      - ./mysql_data:/var/lib/mysql
    environment:
      MYSQL_ROOT_PASSWORD: 123456
      MYSQL_DATABASE: mydb
    command: 
      --character-set-server=utf8mb4
      --collation-server=utf8mb4_unicode_ci
  web:
    image: my-django-app
    container_name: django-app
    command: bash -c "python manage.py migrate && python manage.py runserver 0.0.0.0:8000"
    ports:
      - "8000:8000"
    depends_on:
      - database

之后执行 docker-compose up -d 命令可以创建容器并运行(-d 代表后台运行),在这里会同时启动 Django 和 MySQL 容器,并且可以将 Django 中定义的数据库模型迁移到 MySQL 数据库上。如果想要停止并删除上述容器,就执行 docker-compose down 命令。如果不想删除容器,只想停止,就执行 docker-compose stop 命令,之后如果又想开始,可以执行 docker-compose start 命令。

上面这个文件中有一个注意点就是在 web 这个 service 下,加了一个 depends_on 参数,代表该 service 依赖于上面的 database。即首先得创建数据库容器,之后才能启动后端服务容器。

但在我的实际操作中,发现 MySQL 容器实际上在刚创建完成时还不能马上提供连接服务,而这个时候 Docker compose 以为 MySQL 服务已经就绪,就会启动 Django 服务并连接数据库,最终的结果是 Django 服务无法连上数据库导致启动失败。

为了避免上述情况的发生,更稳妥的方案是分别启动 MySQL 服务和 Django 服务,在确保 MySQL 服务就绪的情况下,再启动 Django。

为此,首先需要创建一个 Docker 网络,并在之后将两个容器放到同一个网络下使得它们可以连接。创建网络的命令为

docker network create app_net

之后,需要创建两个 docker-compose.yml 文件,用于分别启动 MySQL 和 Django。

第一个文件定义 MySQL 启动配置

version: '3'
networks:
  custom_app_net:
    name: app_net
    external: true
services:
  database: 
    image: mysql:5.7
    container_name: mysql
    ports:
      - "3306:3306"
    volumes:
      - ./mysql_data:/var/lib/mysql
    environment:
      MYSQL_ROOT_PASSWORD: 123456
      MYSQL_DATABASE: mydb
    command: 
      --character-set-server=utf8mb4
      --collation-server=utf8mb4_unicode_ci
    networks:
      - custom_app_net

执行 docker-compose up -d 命令后,确保 MySQL 服务启动成功后,再启动 Django 服务,对应的 docker-compose.yml 文件为:

version: '3'
networks:
  custom_app_net:
    name: app_net
    external: true
services:
  web:
    image: my-django-app
    container_name: django-app
    command: bash -c "python manage.py migrate && python manage.py runserver 0.0.0.0:8000"
    ports:
      - "8000:8000"
    depends_on:
      - database
    networks:
      - custom_app_net

再执行 docker-compose up -d 来启动 Django 服务。这样就可以避免 MySQL 还未就绪就被 Django 抢连的情况。

  • Docker

    Docker 是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的容器中,然后发布到任何流行的操作系统上。容器完全使用沙箱机制,几乎没有性能开销,可以很容易地在机器和数据中心中运行。

    490 引用 • 916 回帖 • 2 关注
  • MySQL

    MySQL 是一个关系型数据库管理系统,由瑞典 MySQL AB 公司开发,目前属于 Oracle 公司。MySQL 是最流行的关系型数据库管理系统之一。

    676 引用 • 535 回帖
  • Django
    47 引用 • 72 回帖 • 4 关注

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...
  • aopstudio
    作者

    具体操作我也不太懂,但我感觉应该可以再用一个容器部署 nginx,之后再配置这个 nginx 做反向代理

    1 回复
  • 其他回帖
  • buertang

    有什么办法反代后通过 https 和 navicat 访问数据库吗?

    1 回复
  • buertang

    是的,只有这样才行。