Ubuntu22.04.2 LTS下使用Nginx+Docker+Gunicorn+Python3.11部署Fastapi,并开启SSL

2023/05/15 Docker Gunicorn Nginx Fastapi Ubuntu 共 5692 字,约 17 分钟
一切随猿

一、情景导入

近日开始把公司的项目进行重构,项目之前都是部署到python3自带的虚拟环境中,感觉很麻烦,而且频繁碰壁,最明显的就是在执行python3 -m venv 虚拟环境路径创建虚拟环境的时候,经常报:'Error: Command '['/www/venv/xxx/bin/python3.11', '-m', 'ensurepip', '--upgrade', '--default-pip']' returned non-zero exit status 1.错误,虽然加入--without-pip,但是后续还有很多坑,所需我们索性改为部署到Docker中,虽然在Docker部署的时候,有时候Docker执行pip安装包会报超时,但是重新执行一下Docker构建命令基本都能解决

二、关键词

  • Ubuntu22.04.2
  • Docker容器
  • Gunicorn
  • Nginx
  • 部署Fastapi

三、分析思路

四、工具环境

  • MacOS 10.15.7
  • Ubuntu 22.04.2 LTS
  • Docker 23.0.6
  • Python 3.11.x
  • Fastapi 0.95.0

五、实现步骤

  1. 上传项目到服务器
  2. 安装Docker
  3. 构建Docker容器
  4. 运行Docker
  5. 配置nginx文件
  6. 启动宿主机的nginx服务
  7. 打开浏览器测试访问

六、实操代码

  1. 安装Docker:
    • 执行sudo apt update,更新软件包列表
    • 执行sudo apt install apt-transport-https ca-certificates curl software-properties-common,安装依赖包以允许使用 HTTPS 通过 apt 访问 Docker 仓库
    • 执行curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg,添加 Docker 官方 GPG 密钥
    • 执行echo "deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null,添加 Docker 软件源
    • 执行sudo apt update,更新软件包列表
    • 执行sudo apt install docker-ce docker-ce-cli containerd.io,安装 Docker
    • 执行sudo service docker start,启动 Docker 服务
    • 执行sudo docker run hello-world,如果一切顺利,你将看到一个 Hello World 的提示消息,表示 Docker 已经成功安装并运行
  2. 拷贝项目到服务器:
    • 可以通过SCP或者FTP把项目上传到服务器的/www/server路径下,并执行cd /www/server/你的项目路径打开这个路径中的项目
  3. 构建Docker容器:
    • 编写Gunicorn配置文件: 在项目的config文件夹下新建一个名称为gconfig.py的文件,并填入如下代码:
        import multiprocessing
        import os
      
        # 项目根目录路径
        base_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
        proc_name = 'gunicorn_education_project'  # 进程名
        timeout = 120  # 设置超时时间120s,默认为30s。按自己的需求进行设置timeout = 120
        debug = True
        # 修改代码时自动重启
        reload = True
        #
        # reload_engine = 'inotify'
        # reload_extra_files = []
        # //绑定与Nginx通信的端口
        bind = '0.0.0.0:8000'
        # bind = 'unix:/tmp/gunicorn.sock'
        # pidfile = './log/gunicorn.pid'
      
        # workers = 4  # 进程数
        workers = multiprocessing.cpu_count() * 2 + 1  # 进程数
        threads = 3  # 指定每个进程开启的线程数
      
        worker_class = 'uvicorn.workers.UvicornWorker'  # 默认为阻塞模式,最好选择gevent模式,默认的是sync模式
        # 日志级别
        # debug:调试级别,记录的信息最多;
        # info:普通级别;
        # warning:警告消息;
        # error:错误消息;
        # critical:严重错误消息;
        loglevel = 'debug'
      
        # 日志目录路径
        log_dir = os.path.join(base_dir, 'log', 'gunicorn')
        # 访问日志路径
        accesslog = os.path.join(log_dir, 'access.log')
        # 错误日志路径
        errorlog = os.path.join(log_dir, 'error.log')
      
        # 设置gunicorn访问日志格式,错误日志无法设置
        access_log_format = '%(t)s %(p)s %(h)s "%(r)s" %(s)s %(L)s %(b)s %(f)s" "%(a)s"'
      
    • 编写Docker配置文件: 在项目的根目录下新建一个名称为Dockerfile的文件,并填入如下代码:
        # 使用 Python 3.11 作为基础镜像
        FROM python:3.11
      
        # 设置工作目录
        WORKDIR /app
      
        # 复制项目文件到容器中
        COPY . /app
      
        # 安装项目依赖
        # --no-cache-dir 代表不使用缓存来安装 Python 包
        RUN pip install --no-cache-dir -r requirements.txt
      
        # 安装 Gunicorn
        RUN pip install gunicorn
      
        # 创建日志目录
        RUN mkdir -p /app/log/gunicorn
      
        # 设置日志目录权限
        RUN chmod -R 777 /app/log/gunicorn
      
        # 暴露端口
        EXPOSE 8000
              
        # 启动Gunicorn
        CMD ["gunicorn", "main:app", "-c", "config/gconfig.py"]
      
    • 构建容器:
      • 执行cd /www/server/xxx命令,切换到项目路径
      • 执行sudo docker build -t xxx .命令,开始构建容器 -t xxx:指定了要构建的镜像的名称和标签。-t 是标签的缩写,xxx 是镜像的名称。可以根据需要修改名称和标签。 .:表示当前目录,它告诉 Docker 在当前目录中查找 Dockerfile 文件并使用它来构建镜像。
  4. 运行Docker:
    • 执行sudo docker run -d -p 9090:8000 --name aaa xxx命令,这将在后台运行一个名为 xxx 的 Docker 容器,并将主机的 9090 端口映射到容器的 8000 端口
      • -d:是一个选项,表示在后台模式下运行容器,docker
      • -p 9090:8000:可以将主机的9090端口与容器的8000端口进行映射,Dockerfile文件中的EXPOSE命令只是声明容器内部的端口,但并不会自动进行主机端口的映射
      • aaa:为容器指定一个可识别的名称
      • xxx:要运行的 Docker 镜像的名称。通过指定镜像名称,Docker 将在该镜像的基础上创建并运行一个容器,这个xxx与sudo docker build -t xxx .中的xxx是相关的,通过构建镜像并为其指定名称后,可以使用该名称来运行该镜像创建的容器。
  5. 配置nginx文件:
    • 在宿主机的/etc/nginx/conf.d路径下新建一个xxx.conf文件,并填入如下代码:
        upstream renren_education {
            server 127.0.0.1:9090 fail_timeout=100;
        }
              
        server {
            listen 80;
            server_name yourdomain.com; #需要将yourdomain.com替换成证书绑定的域名。
            rewrite ^(.*)$ https://$host$1; #将所有HTTP请求通过rewrite指令重定向到HTTPS。
            location / {
                index index.html index.htm;
            }
        }
      
        server {
            listen 443 ssl;
            #配置HTTPS的默认访问端口为443。
            #如果未在此处配置HTTPS的默认访问端口,可能会造成Nginx无法启动。
            #如果您使用Nginx 1.15.0及以上版本,请使用listen 443 ssl代替listen 443和ssl on。
            server_name yourdomain.com; #需要将yourdomain.com替换成证书绑定的域名。
            root /www/server/xxx;
            ssl_certificate /etc/nginx/cert/xxx.pem;  #需要将cert-file-name.pem替换成已上传的证书文件的名称。
            ssl_certificate_key /etc/nginx/cert/xxx.key; #需要将cert-file-name.key替换成已上传的证书私钥文件的名称。
            ssl_session_timeout 5m;
            ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4;
            #表示使用的加密套件的类型。
            ssl_protocols TLSv1 TLSv1.1 TLSv1.2; #表示使用的TLS协议的类型。
            ssl_prefer_server_ciphers on;
      
            location / {
                proxy_set_header x-Real-IP $remote_addr;
                proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
                proxy_set_header Host $http_host;
                include proxy_params;
                proxy_pass http://renren_education;
            }
        }
      
  6. 启动宿主机的nginx服务:
    • 执行server nginx reload命令,进行重启nginx服务
  7. 打开浏览器测试访问:
    • 打开浏览器输入yourdomain.com即可访问到部署的应用

七、归纳总结

八、注意事项

  1. 虽然我在Dockerfile文件中配置了gunicorn的日志路径,但是在启动gconfig.py文件的时候,还是会报Error: Error: '/app/log/gunicorn/error.log' isn't writable [FileNotFoundError(2, 'No such file or directory')],后来在项目里面直接创建了log/gunicorn路径后重启docker,就离奇的好了,暂时未知原因,先埋一个坑

  2. 如果项目文件有更新,但是不更新容器的配置文件,可以执行sudo docker run -d -p 9090:8000 --name 容器名称 -v /www/server/xxx:/app 容器镜像名称命令进行挂载更新,-v /www/server/xxx:/app 容器镜像名称部分是用于将本地的 /www/server/xxx 目录挂载到容器内的 /app 目录,并指定镜像名称,以实现主机和容器之间的文件共享,这样,新的项目文件将被复制到容器中,并且容器将在更新后重新启动。如果发现项目没有更新,可以执行sudo docker restart 容器名称来重启项目

  3. Dockerfile文件中的WORKDIR /app目录配置要跟 COPY . /app的目标目录保持一致

  4. 如果项目的数据库跟Docker容器在同一个服务器上,项目里面直接配置服务器的内网地址即可访问该数据库

  5. 如果要实时查看docker内部产生的日志,需要使用标准的logging库进行日志打印

九、相关资源

  • sudo docker build -t 容器名称 .,构建容器的命令,.代表需要构建的路径,一般是含有Dockerfile配置文件的路径
  • sudo docker -d -p 宿主机端口:容器端口 -v 项目路径:容器工作目录-d是一个选项,表示在后台模式下运行容器,-p指定了端口映射的规则
  • sudo docker images, 这将列出所有已构建的 Docker 镜像
  • sudo docker ps,列出当前正在运行的容器
  • sudo docker ps -a,查看所有容器的状态
  • sudo docker start <容器ID或容器名称>, 启动该容器
  • sudo docker stop <容器ID或容器名称>,停止该容器
  • sudo docker rm <容器ID或容器名称>,删除该容器
  • sudo docker restart <容器ID或容器名称>,重新启动容器
  • sudo docker rmi <容器名称>,删除容器镜像
  • sudo docker logs <容器名称>,可以查看容器的日志
  • sudo docker logs -f <容器名称>,可以实时查看容器的日志
  • sudo truncate -s 0 /var/lib/docker/containers/<container_id>/<container_id>-json.log,可以清空该容器的日志
  • sudo service docker status,可以查看docker的运行状态
  • sudo docker cp <容器ID>:/log/gunicorn/access.log /www/log/gunicorn/access.log,可以将容器中的/log/gunicorn/access.log文件导出到宿主机的/www/log/gunicorn/access.log文件中

文档信息

搜索内容

    文章目录