Fastapi框架-冷饭再炒-(14)-为服务docker化-重学备忘录docker中篇

1,591 阅读13分钟

一 、基于官网的Dockerfile 构建镜像

概念

Dockerfile基于一系列的脚本或命令重新构建一个新的镜像。当然最基本的这些脚本或命令的最基础的底层基础镜像(一般是具有操作系统级别封装好的基础镜像:如centos,再nginx镜像中我们进入之后查看内部的结构可以看到它对于的目录都有相关的操作系统级别的目录结构,所以可以说明所有的镜像的基础镜像一般都是具有操作系统级别的基础镜像)。

镜像构建场景

  • 1:对开发人员提供一致性的开发环境。

  • 2:运维人员部署实现无缝移植。

  • 3:测试人员基于已构建的镜像做相关的扩展或构建新的镜像。

常用命令

命令作用
FROM IMAGE_NAME:TAG基于哪个基础的镜像启动构建流程(前提需镜像需先存在,不然会去pull)
MAINTAINER USER_NAME声明镜像创建者(无关紧要,做一些备注或版权说明)
ENV ke vlaue设置当前镜像的环境的变量,(可以写多个设置)
RUN COMMAND命令的相关的执行,是Dockerfile核心部分 (可以写多条)
ADD source_dir/file dst_dir/dir把宿主机的文件复制到容器内,(如果复制的文件是一个压缩的文件,将会在复制后自动的解压)
COPY source_dir/file dst_dir/dir把宿主机的文件复制到容器内,(但如果复制的文件是一个压缩的文件,不会自动的解压)
WORKDIR parh_dir设置工作目录,此工作目录后续的所有的命令的执行都是再目录下进行
CMD运行程序或命令

CMD的补充说明:

CMD
类似于 RUN 指令,用于运行程序,但二者运行的时间点不同:

CMD 在docker run 时运行。
RUN 是在 docker build。
作用:为启动的容器指定默认要运行的程序,程序运行结束,容器也就结束。CMD 指令指定的程序可被 docker run 命令行参数中指定要运行的程序所覆盖。

注意:如果 Dockerfile 中如果存在多个 CMD 指令,仅最后一个生效。

最简实践fastapi docker服务化

  • 1)项目结构规划:

image.png

  • 2)编写Dockerfile文件(基于fastapi提供的基础镜像) 方式1:
FROM tiangolo/uvicorn-gunicorn-fastapi:python3.7

COPY ./main.py /app

image.png

上面COPY ./main.py /app 表示的是,复制当前的目录下main.py 到容器内部的/app目录下

  • 3)上传项目到对于的目录下用于构建

image.png

  • 4)进如到对于的docker_fastapi目录下,然后执行镜像构建

PS : 注意后面有一个点 是表示查询当前Dockerfile文件,因为是在当前目录下,所以直接的 使用 ‘.’

[root@localhost docker_fastapi]# docker build -t dfastapi .

查看具体构建成功的镜像文件:

[root@localhost docker_fastapi]# docker image ls -a | grep fast
dfastapi                            latest      e75f29899cf9   23 minutes ago
tiangolo/uvicorn-gunicorn-fastapi   python3.7   86ade7dea2c7   7 weeks ago
[root@localhost docker_fastapi]#

  • 5)构建完成后,进行相关的镜像容器启动的测试 查看构建的镜像:

[root@localhost docker_fastapi]# docker run -d --name myfastapiserve -p 80:80 dfastapi

  • --name myfastapiserve 容器的名称是myfastapiserve
  • -p 80:80 端口映射

临时启动测试,可以用于查看内部的启动,方便预览服务启动是否报错 关键点: -it --rm


[root@localhost docker_fastapi]# docker run -it --rm -p 80:80 dfastapi
Checking for script in /app/prestart.sh
Running script /app/prestart.sh
Running inside /app/prestart.sh, you could add migrations to this file, e.g.:

#! /usr/bin/env bash

# Let the DB start
sleep 10;
# Run migrations
alembic upgrade head

{"loglevel": "info", "workers": 2, "bind": "0.0.0.0:80", "graceful_timeout": 120, "timeout": 120, "keepalive": 5, "errorlog": "-", "accesslog": "-", "workers_per_core": 1.0, "use_max_workers": null, "host": "0.0.0.0", "port": "80"}
[2021-08-02 04:16:07 +0000] [1] [INFO] Starting gunicorn 20.0.4
[2021-08-02 04:16:07 +0000] [1] [INFO] Listening at: http://0.0.0.0:80 (1)
[2021-08-02 04:16:07 +0000] [1] [INFO] Using worker: uvicorn.workers.UvicornWorker

补充非临时启动的情况下 ctrl+c强制结束再次启动

docker container start 容器的名称(缩写的话,container可以不要:docker stop 容器的名称)
docker container restart 容器的名称(缩写的话,container可以不要:docker stop 容器的名称)
docker container stop 容器的名称(缩写的话,container可以不要:docker stop 容器的名称)

6、访问服务地址验证

image.png

7、基于运行中的容器使用docker commit命令构建新的镜像

查看当前已经创出的容器列表


[root@localhost docker_fastapi]# docker ps -a
CONTAINER ID   IMAGE          COMMAND                  CREATED          STATUS                      PORTS     NAMES
5dbce938447b   dfastapi       "/start.sh"              32 minutes ago   Exited (0) 18 minutes ago             myfastapiserve
3bd91733a27c   ubuntu:15.10   "/bin/echo 'Hello wo…"   2 days ago       Exited (0) 2 days ago                 pedantic_dirac
86eb9e903b4a   hello-world    "/hello"                 2 days ago       Exited (0) 2 days ago                 objective_nobel
[root@localhost docker_fastapi]#

基于已有的容器创建新的镜像

[root@localhost docker_fastapi]# docker commit -m "fastapiserve_image" -a "xiaozhong" 5dbc zyxfastapi:v1
sha256:fd7e914200f3ec5885171c405083341537af46501e5dc8c074405dfdcdddd177
[root@localhost docker_fastapi]#

参数解释:

  • -m选项指定了新镜像的提交信息,
  • -a标注作者信息,
  • b66是容器ID
  • myubuntu:v1是指定的新镜像名称。

查看生成的新的镜像文件:

image.png

8、基于新的镜像跑新的容器实例测试验证

用新的镜像运行新的容器:

[root@localhost docker_fastapi]# docker run -d --name zyxfastapi_contacin -p 80:80 zyxfastapi:v1
4f1951c2582fa2e146b10b8d43b1293215a0445b3e7682dfc7a82793854dc737
[root@localhost docker_fastapi]#

测试验证:

image.png

其他测试:


[root@localhost docker_fastapi]# docker rm 4f
4f
[root@localhost docker_fastapi]# docker run -d --name zyxfastapi_contacin -p 8080:80 zyxfastapi:v1
0f3ba7a5c1e910ec74ad7f334e612f21f1858554f8b996fdb0ea5400efdcae35
[root@localhost docker_fastapi]#

二 、自定义的Dockerfile 构建镜像

1:项目脚手架的:

image.png

2:服务的启动的入口main.py文件:

#!/usr/bin/evn python
# -*- coding: utf-8 -*-
"""
-------------------------------------------------
   文件名称 :     main
   文件功能描述 :   功能描述
   创建人 :       小钟同学
   创建时间 :          2021/7/15
-------------------------------------------------
   修改描述-2021/7/15:         
-------------------------------------------------
"""

from apps.app import FastSkeletonApp

def create_app():
    '''
    创建我们的Fastapi对象
    :return:
    '''
    # from apps.middleware.logroute import ContextIncludedRoute
    # self.app.router.route_class = ContextIncludedRoute

    app = FastSkeletonApp()
    # 返回fastapi的App对象
    return app.startge



app =create_app()

if __name__ == '__main__':
    # 启动服务
    from apps.utils.routes_helper import print_all_routes_info
    print_all_routes_info(app)


    # 上面这种方式无法查询到使用ApiROUD进来的路由
    # ,log_level=1000这个配置关闭日志的无效
    #  # log_level="info",
    # # log_config=LOGGING_CONFIG,
    import uvicorn

    uvicorn.run('main:app', port=8080, debug=True, reload=True, access_log=True,workers=1, use_colors=True)

3:编写Dockerfile

FROM python:3.7

WORKDIR /app

COPY requirements.txt ./

RUN pip install --no-cache-dir --default-timeout=100  -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple

COPY . .

CMD [ "python", "./main.py" ]

4:上传到我们的服务器上(生成镜像)

image.png

5: 进入到目录下执行镜像生成

[root@localhost fastapi_framework_skeleton]#  docker build -t fsfastapi .

image.png

6:启动镜像的一个容器测试验证

启动的测试的可以看日志的容器,用于排错!

[root@localhost fastapi_framework_skeleton]# docker run -it --rm --name zidingfas -p 80:80 fsfastapi

错误一堆:

image.png

7:挂载外部文件进行排错

查看具体的日志信息

image.png

主要原因是,我的脚手架的框架启动的时候,需要连接到我们的本地的redis服务,但是容器内的是没有这个redis服务滴,所以会导致链接失败!

尝试先注释redis的连接,后续再用多个容器进行编排串联整个服务的部署。

修改后删除镜像重新生成一下新的镜像:


[root@localhost fastapi_framework_skeleton]# docker rmi fsfastapi

启动依然有错,错误的问题是:

# 没有捕获asyncio.CancelledError

为排错,避免一直镜像的重新生成,使用挂载的方式进行外部文件修改映射到内部的容器的方式进行测试:

启动挂载的启动的方式:

[root@localhost fastapi_framework_skeleton]# docker run --name zidingfas -v /data/server/fastapi_framework_skeleton/:/app -it --rm  -p 80:80 fsfastapi

8:改错

错误的愿意是我的再进行事件处理的异步处理的时候,进行批量的任务的取消的操作的时候出现的错误异常!

from fastapi import FastAPI
from apps.ext.logger import logger
import signal
import asyncio
from apps.utils.singleton_helper import Singleton


@Singleton
class ExitAppCleanTasksHandler():

   def __init__(self, app=None, *args, **kwargs):
       super().__init__(*args, **kwargs)
       if app is not None:
           self.init_app(app)

   def init_app(self, app: FastAPI):
       pass

       @app.on_event("startup")
       async def startup():
           pass
           logger.info('应用启动!')
           loop = asyncio.get_event_loop()
           try:
               signals = (signal.SIGHUP, signal.SIGTERM, signal.SIGINT)
               for s in signals:
                   loop.add_signal_handler(s, lambda s=s: asyncio.create_task(shutdown(s)))
               logger.info('startup end')
           except AttributeError:
               import warnings
               # warnings.warn("系统兼容问题,没有对应的信号属性!")

       @app.on_event("shutdown")
       async def shutdown(signal: signal):
           try:
               # 这个地方反而处理会引发一个没有捕获asyncio.CancelledError
               logger.info("Received exit signal %s...", signal.name)
               tasks = [t for t in asyncio.all_tasks() if t is not asyncio.current_task()]
               [task.cancel() for task in tasks]
               logger.info("Canceling outstanding tasks")
               await asyncio.gather(*tasks)
           except:
               pass

其实这个了是没必要进行这样的批量的操作退出滴!注释进行插件的初始化后就正常了!!!

另外启动的时候,我们的启动的是127.0.0.1的的内部,这个地方也需要注意修改一下:

image.png

修改我们的main的文件:

if __name__ == '__main__':
    # 启动服务
    from apps.utils.routes_helper import print_all_routes_info
    print_all_routes_info(app)


    # 上面这种方式无法查询到使用ApiROUD进来的路由
    # ,log_level=1000这个配置关闭日志的无效
    #  # log_level="info",
    # # log_config=LOGGING_CONFIG,
    import uvicorn

    uvicorn.run('main:app',host='0.0.0.0', port=8080, debug=True, reload=True, access_log=True,workers=1, use_colors=True)

9:再次启动测试:

修改后的启动:


[root@localhost fastapi_framework_skeleton]# docker run --name zidingfas -v /data/server/fastapi_framework_skeleton/:/app -it --rm  -p 8080:8080 fsfastapi

查看启动的显示: image.png

此时可以正常访问了!

image.png

image.png

但是问题是我们的服务访问一次就重启一次,这有问题:

image.png

原因还是因为我们的开启监听模式,监听文件有变化就重启的!

所以修改我们的main文件: image.png

再测就完美了!

10 :总结

上述的部署,仅仅只是把我们的项目进行了打包到docker里面,但是我们的项目里面里使用到的还有redis还有postgresql等的服务,他们是相互依赖才可以正常的访问,这种情况下,我们的就需要把所有的服务启动起来,但是能不能把所有的服务都进行docker化呐?然后容器内相互的进行访问,这个毫无疑问必须是可以的啊!哈哈

另外上面的方式缺点: 1:镜像文件贼大 2:无法再容器内部访问redis,因为redis还有数据库等。

三 、# docker compose 服务编排

3.1 作用

docker compose 是基于python开发的,用于Docker的服务编排工具,在构建基于Docker的复杂应用的时候通过Compose编写docker-compose.yml一个配置文件来管理多个Docker容器,或对容器集群的管理和编排,非常适合组合使用多个容器进行开发的场景。

Dockerfile 用来构建 Docker 镜像,那么 docker-compose 则是用来创建容器。 docker-compose一般是主要用于我们在构建 Docker Run 的一些了的命令封装执行等的问题。

3.2 作用示例说明

通常我们的一个完整的项目其实是需要整合几个服务的节点包括App服务,数据库服务,还有缓存服务等,我们再进行项目初期的开发的时候,我们为了开发环境的方便的测试,其实可以编排好我们的服务节点,把所有的服务都整合到一个容器内运行,这样也方便我们的开发环境的迁移和构建,不需要话费太多的时间再环境的搭建上。
当然线上生产环境,肯定是不推荐这么搞得,毕竟,本身Docker存在的目的就是为了服务独立和环境隔离!

测试环境搭建:

\

image.png

线上环境的话肯定都是独立的容器

3.3 docker compose安装

1下载安装:

[root@localhost ~]# curl -L https://get.daocloud.io/docker/compose/releases/download/1.24.0/docker-compose-`uname -s`-`uname -m` > /usr/local/bin/docker-compose
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   423  100   423    0     0    221      0  0:00:01  0:00:01 --:--:--   221
100 15.4M  100 15.4M    0     0  4427k      0  0:00:03  0:00:03 --:--:-- 10.0M

2:给权限
[root@localhost ~]# chmod +x /usr/local/bin/docker-compose

可以给软件接
创建软链:
 [root@localhost ~]#  ln -s /usr/local/bin/docker-compose /usr/bin/docker-compose


3:查看版本信息
[root@localhost ~]# docker-compose version
docker-compose version 1.24.0, build 0aa59064
docker-py version: 3.7.2
CPython version: 3.6.8
OpenSSL version: OpenSSL 1.1.0j  20 Nov 2018


[root@localhost ~]# docker-compose -v
docker-compose version 1.24.0, build 0aa59064
[root@localhost ~]#

3.4 docker compose 命令

[root@localhost web_statistics]#  docker-compose
Define and run multi-container applications with Docker.

Usage:
  docker-compose [-f <arg>...] [options] [COMMAND] [ARGS...]
  docker-compose -h|--help

Options:
  -f, --file FILE             Specify an alternate compose file
                              (default: docker-compose.yml)
  -p, --project-name NAME     Specify an alternate project name
                              (default: directory name)
  --verbose                   Show more output
  --log-level LEVEL           Set log level (DEBUG, INFO, WARNING, ERROR, CRITICAL)
  --no-ansi                   Do not print ANSI control characters
  -v, --version               Print version and exit
  -H, --host HOST             Daemon socket to connect to

  --tls                       Use TLS; implied by --tlsverify
  --tlscacert CA_PATH         Trust certs signed only by this CA
  --tlscert CLIENT_CERT_PATH  Path to TLS certificate file
  --tlskey TLS_KEY_PATH       Path to TLS key file
  --tlsverify                 Use TLS and verify the remote
  --skip-hostname-check       Don't check the daemon's hostname against the
                              name specified in the client certificate
  --project-directory PATH    Specify an alternate working directory
                              (default: the path of the Compose file)
  --compatibility             If set, Compose will attempt to convert keys
                              in v3 files to their non-Swarm equivalent

Commands:
  build              Build or rebuild services
  bundle             Generate a Docker bundle from the Compose file
  config             Validate and view the Compose file
  create             Create services
  down               Stop and remove containers, networks, images, and volumes
  events             Receive real time events from containers
  exec               Execute a command in a running container
  help               Get help on a command
  images             List images
  kill               Kill containers
  logs               View output from containers
  pause              Pause services
  port               Print the public port for a port binding
  ps                 List containers
  pull               Pull service images
  push               Push service images
  restart            Restart services
  rm                 Remove stopped containers
  run                Run a one-off command
  scale              Set number of containers for a service
  start              Start services
  stop               Stop services
  top                Display the running processes
  unpause            Unpause services
  up                 Create and start containers
  version            Show the Docker-Compose version information
[root@localhost web_statistics]#

扩展记录:

## 启动服务

docker-compose -f docker-compose.yml up -d



## 停止服务

docker-compose -f docker-compose.yml stop


## 停止并删除服务

docker-compose -f docker-compose.yml down

3.5 docker compose 启动两个fastapi服务示例

为了方便测试,把本地的相关的镜像和容器等都先删了!

docker rm $(docker ps -aq) #删除所有的容器
docker rmi $(docker images -q)#删除所有的镜像

然后重新的生成新的一个fastapi镜像:

image.png

吓一跳了吧|镜像文件贼大!后面肯定是需要优化滴!

有了镜像后,开始一个小实践跑两个服务的容器实例.

3.5.1 文件编排

version: "3.3"
services:
    fastapi_web_01:
        image: fastapi_web:latest
        build: .
        container_name: fastapi_web_api_01
        restart: always
        ports:
            - "1245:8080"
    fastapi_web_02:
        image: fastapi_web:latest
        build: .
          container_name: fastapi_web_api_02
          restart: always
          ports:
          - "1246:8080"

3.5.2 上传文件

image.png

3.5.3使用docker compose的文件进行启动容器

进入到当前上传的项目目录下,执行:

[root@localhost fastapi_framework_skeleton]# docker-compose up
ERROR: yaml.scanner.ScannerError: mapping values are not allowed here

错误提示:

image.png

主要的问题是yaml文件格式的问题!

需要对齐!

version: "3.3"
services:
    fastapi_web_01:
        image: fastapi_web:latest
        build: .
        container_name: fastapi_web_api_01
        restart: always
        ports:
            - "1245:8080"
    fastapi_web_02:
        image: fastapi_web:latest
        build: .
        container_name: fastapi_web_api_02
        restart: always
        ports:
            - "1246:8080"

image.png

等待处理构建和启动:

image.png

3.5.4 访问验证

image.png

image.png

总结:

以上仅仅是个人结合自己的实际需求,做学习的实践笔记!如有笔误!欢迎批评指正!感谢各位大佬!

结尾

END

简书:www.jianshu.com/u/d6960089b…

掘金:juejin.cn/user/296393…

公众号:微信搜【小儿来一壶枸杞酒泡茶】

小钟同学 | 文 【原创】【欢迎一起学习交流】| QQ:308711822