Docker部署python项目
目的:为了解决生产环境和开发环境的环境版本问题,同时也是因为不同服务器中的git库同步不方便的原因所以使用docker进行开发部署。
- 最终结论是选择拉取基础镜像直接在镜像中进行开发最为方便
开发部署流程
- 方式一:直接在docker容器中开发
- 操作:启动一个基础镜像的容器,进入容器内部安装依赖、写代码、调试。
- 特点:
- 方式二:本地开发+docker打包部署
- 操作:在本地python环境进行开发,开发完成后通过Dockerfile构建镜像生产镜像。
- 特点:
开发案例
方案一
- 实验环境 |macOS|Docker version 28.0.1, build 068a01e``
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
|
docker pull python:3.11-slim-bullseye
''' services: app: image: python:3.11-slim-bullseye container_name: python-app working_dir: /app volumes: - ./config.py:/app/config.py - ./app:/app ports: - "8015:8015" command: tail -f /dev/null restart: unless-stopped ''' docker-compose up
docker exec -it python-app bash
$ docker login --username=stylite灬玉京子 crpi-iodthkq5b3ol4fv7.cn-chengdu.personal.cr.aliyuncs.com $ docker tag [ImageId] crpi-iodthkq5b3ol4fv7.cn-chengdu.personal.cr.aliyuncs.com/dummy-v07/dummy_space:[镜像版本号] $ docker push crpi-iodthkq5b3ol4fv7.cn-chengdu.personal.cr.aliyuncs.com/dummy-v07/dummy_space:[镜像版本号]
|
📍 必须预先定义的容器参数
目录挂载|工作路径|端口暴露|交互模式
🧩 推荐的高级参数
环境变量|容器名称|自动清理
1 2 3 4 5 6 7 8
| -e PYTHONUNBUFFERED=1 \ -e DEBUG=1 \ -e PYTHONDONTWRITEBYTECODE=1
--name my-python-dev
--rm
|
方案二
整体体验感并不好。。。
项目开发完毕后写一个Dockerfile
EXPOSE 8000 左右:声明容器内部应用程序监听的端口 告诉
8000:8000 “宿主机端口:容器端口” 端口映射,将宿主机的端口映射到容器内的端口,将宿主机的8000端口映射到容器的8000端口
Dockerfile
构建自定义镜像主要是为了满足特定应用场景的需求,克服使用通用或官方镜像时可能遇到的局限性。
DockerFile文件是指导docker进行打包的文档:
FROM : 指定运行环境
WORKEIR:指定工作目录
COPY:拷贝指令
RUN:预运行指令
CMD:CMD运行指令
基本命令
docker build -t xxx 路径 将该路径建立为docker镜像
使用Docker
- 构建镜像:
1
| docker build -t oil-meter-api .
|
构建镜像的时候可能会遇见下载慢或者下载直接失败的情况,所以经常可能需要换源。
- 运行容器:
1
| docker run -p 8000:8000 oil-meter-api
|
Docker换源
- 更换docker镜像源。这里由于docker下载东西从国外的服务器很慢,因此要换成国内的镜像源。
- 打开/etc/docker/daemon.json文件,sudo gedit /etc/docker/daemon.json,发现是空白的,添加以下内容:
- {“registry-mirrors”:[“http://docker.m.daocloud.io"]}
- Systemctl restart docker
Docker compose
docker-compose.json
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240
|
version: '3.8'
services: oil-meter-api: build: context: . dockerfile: Dockerfile args: BUILDKIT_INLINE_CACHE: 1 ports: - "8000:8000" - "8001:8000" environment: - PYTHONUNBUFFERED=1 - DEBUG=false - LOG_LEVEL=INFO - API_KEY=your_api_key - DATABASE_URL=postgresql://user:pass@db:5432/db env_file: - .env - .env.production volumes: - ./logs:/app/logs - ./uploads:/app/uploads - ./config:/app/config:ro - postgres_data:/var/lib/postgresql/data networks: - frontend - backend depends_on: - postgres - redis restart: unless-stopped deploy: resources: limits: memory: 1G cpus: '1.0' reservations: memory: 512M cpus: '0.5' replicas: 1 update_config: parallelism: 1 delay: 10s failure_action: rollback restart_policy: condition: on-failure delay: 5s max_attempts: 3 window: 120s healthcheck: test: ["CMD", "curl", "-f", "http://localhost:8000/health"] interval: 30s timeout: 10s retries: 3 start_period: 40s disable: false container_name: oil-meter-api hostname: api-server user: "1000:1000" working_dir: /app command: ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "8000", "--reload"] labels: - "com.example.description=Oil Meter API" - "com.example.department=engineering" - "com.example.version=1.0" logging: driver: "json-file" options: max-size: "10m" max-file: "3" security_opt: - no-new-privileges:true devices: - "/dev/ttyUSB0:/dev/ttyUSB0" extra_hosts: - "host.docker.internal:host-gateway" - "db-server:192.168.1.100" tmpfs: - /tmp:size=100m read_only: true tmpfs: - /tmp:size=100m - /var/tmp:size=100m
postgres: image: postgres:13-alpine environment: POSTGRES_DB: oilmeter POSTGRES_USER: oiluser POSTGRES_PASSWORD: oilpass volumes: - postgres_data:/var/lib/postgresql/data ports: - "5432:5432" networks: - backend restart: unless-stopped healthcheck: test: ["CMD-SHELL", "pg_isready -U oiluser -d oilmeter"] interval: 10s timeout: 5s retries: 5
redis: image: redis:6-alpine ports: - "6379:6379" volumes: - redis_data:/data networks: - backend restart: unless-stopped command: redis-server --appendonly yes healthcheck: test: ["CMD", "redis-cli", "ping"] interval: 10s timeout: 5s retries: 5
networks: frontend: driver: bridge backend: driver: bridge internal: true
volumes: postgres_data: driver: local redis_data: driver: local
configs: app_config: file: ./config/app.yml
secrets: api_key: file: ./secrets/api_key.txt
|
阿里云云镜像仓库
https://cr.console.aliyun.com/repository/cn-chengdu/dummy-v07/dummy_space/details
操作和git大同小异很简单,就不再赘述
copy和volumes的区别
# Dockerfile1 2 3
| COPY app.py . COPY requirements.txt .
|
- copy:是在docker build(构建)时复制到镜像中,成为镜像的一部分,具有不可变性(镜像),文件与镜像版本绑定
- 实用场景:应用代码;依赖文件;静态资源;模版文件
- copy建议:核心部分代码,需要版本控制,不需要频繁修改
1 2 3 4
| volumes: - ./logs:/app/logs - ./uploads:/app/uploads
|
volumes:实在docker run时挂载,文件存储在宿主机,可以随时修改文件,容器删除后文件保留(性能影响:可能会有I/O开销)
实用场景:日志文件;用户上传文件;数据库文件;配置文件
volumes建议:数据需要持久化,需要频繁修改,多个容器共享数据