目录
一、概述
- 简介:Docker 是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的容器中,然后发布到任何流行的Linux或Windows操作系统的机器上,简而言之,docker可以实现以应用为核心的最精简虚拟机。
- 名词解释
- 守护进程:Docker Daemon
- 镜像:重点,Docker Image,类似于面向对象中的类
- 容器:重点,DockerContainer,类似于面向对象中的类实例(对象)
二、安装docker桌面管理器
2.1 安装
- ubuntu安装:
# 安装docker apt install docker -y # 启动|关闭|重启docker服务 systemctl start|stop|restart docker # 开机启动docker systemctl enable docker # 查看docker当前状态 systemctl status docker # 测试docker是否正确安装及功能完备 docker run hello-world
- mac安装、win10desktop安装:传送门
- docker镜像仓库:传送门,创建账号和镜像版本标签(tag)处
2.2 镜像加速器配置
- win10、mac配置位置
仓库镜像:参见Ubuntu,国内因某些众所不周知的原因,好像大面积停止服务了,多搜搜仓库镜像网址吧
- ubuntu 加速器配置(2024.8.24更新)
sudo mkdir -p /etc/docker sudo tee /etc/docker/daemon.json <<-'EOF' { "registry-mirrors": [ "https://hub.uuuadc.top", "https://docker.anyhub.us.kg", "https://dockerhub.jobcher.com", "https://docker.ckyl.me", "https://docker.awsl9527.cn"] } EOF sudo systemctl daemon-reload sudo systemctl restart docker
仓库镜像:可以网上搜,华为、腾讯都有
三、常用命令
3.1 docker命令
- 命令
# 获取docker版本 docker version # 详细信息,包括容器信息 docker info # 测试docker是否正确安装及功能完备 docker run hello-world
群晖nas:需管理员权限
sudo -i
,获取root权限,密码是登录用户名密码
3.2 镜像命令
- 命令
# 现实镜像列表 docker images # 拉取nginx镜像:pull 镜像名:版本号, # 省略版本号即为latest,镜像都自带系统默认为Ubuntu docker pull nginx:1.20 # 删除镜像 # docker rmi nginx:1.20
- 镜像版本tag查询
ubuntu系统,docker镜像本地存放位置:/var/lib/docker/image/overlay2/imagedb/content/sha256
3.3 容器命令
- 创建及查询容器
# 创建并启动容器: # -d: 后台运行 # -i:容器开启交互接口,若容器内无循环逻辑,则立即exit # --name: 容器别名 # -p: 映射端口,宿主端口:容器虚拟机端口,多映射写法-p x:x -p x:x # 构建容器的最底层镜像:镜像:版本号,若本机没有,docker自动pull # 网络:--net=host与主机共享IP和端口,none无网络,bridge默认虚拟网络 docker run -id --name nginx_test -p 8080:80 nginx:1.20 # docker容器运行必须有一个前台进程, 如果没有前台进程执行,容器 # 认为空闲,就会自行退出,-i(容器开启交互接口) docker run -id --name python3 python:3.9 # 显示已启动的容器详情列表,-a可以显示包括关闭的容器 # 各列说明见下表 docker ps [-a] # 容器重命名 docker rename 原容器名 新容器名 # 拷贝文件:从宿主到容器,container为容器名,也可为容器序号 # 将dir文件夹拷贝到root目录内,注意斜杠 docker cp ./dir container:/root/ # 从容器到宿主机 docker cp container:/root ./dir/
- docker ps结果
表头 数据 释义 CONTAINER ID aa67260f166d 自动分配容器序号 IMAGE nginx:1.20 镜像 COMMAND “/docker-entrypoint.…” 容器启动后立即执行的命令、脚本 CREATED 4 minutes ago 容器创建时间 STATUS Up 4 minutes 容器状态:created(已创建)restarting(重启中)running(运行中)removing(迁移中)paused(暂停)exited(停止)dead(死亡) PORTS 0.0.0.0:8080->80/tcp 容器端口映射,宿主端口=>容器端口/协议 NAMES nginx_test 容器别名 - 容器运维操作
# 容器名:可以用别名,也可以用序号,位数能区分容器即可 # 重启容器:取序号前两位,位数能区分容器即可 docker restart aa # 关闭容器:取容器别名 docker stop nginx_test # 开启容器:取容器前一位 docker start a # 进入容器内: # exec:退出容器,不关闭容器 # -i(容器开启交互接口),-t(创建伪终端) # aa6: 容器序号,从第一位写,只要没冲突就行 # /bin/bash:进入容器后启动的shell docker exec -it aa6 bash # 退出伪终端命令为:exit # 删除容器,配合docker ps -a使用 # docker rm aa
容器内操作系统:基本为Ubuntu,下载工具分别为apt,简单判断看下面哪个命令能运行
网络工具:- Ubuntu:
更新源缓存:apt update
安装网络工具:apt install net-tools
安装ping工具:apt install iputils-ping
安装ssh:apt install openssh-server
/etc/sshd_config取消注释并修改此行PermitRootLogin yes和PasswordAuthentication yes
- Ubuntu:
四、DockerFile编写
范例:封装uwsgi和Django项目到一个镜像里
4.1 前置
- 文件结构
- uwsgi.ini(注释要单独成行)
[uwsgi] # Django 项目相关配置 # Django 项目的根目录 ########################需要修改################################# chdir = /home/www/backend # WSGI 模块路径(在Django的settings.py中WSGI_APPLICATION,注意冒号) module = main.wsgi:application # uWSGI 服务配置(接收NGINX转发过来的http请求, # 也可以直接对外,但请求处理能力有限) http = 0.0.0.0:2048 ################################################################ # 工作进程数量 # 对于轻量级或低流量的应用,你可以设置 processes为 # CPU核心数的1-2 倍,而 threads 可以设置为 2-4 processes = 2 # 每个进程的线程数 threads = 2 # 启用主进程管理模式 master = true # 性能优化 # 每个进程处理的最大请求数,达到后重启进程 max-requests = 2000 # 请求超时秒数,超时的进程将被强制杀死 harakiri = 30 # uWSGI 退出时清理环境 vacuum = true # 启用进程互斥锁,避免僵尸进程 thunder-lock = true # 日志配置 # 日志文件路径,在uwsgi运行目录下 logto = uwsgi.log # 在日志中包含时间戳 log-date = true log-maxsize = 50000 # 支持日志文件轮转 log-reopen = true # 关闭 uWSGI 内部的请求日志 disable-logging = true # 进程 ID 文件路径,在uwsgi运行目录下 pidfile = uwsgi.pid # 设置缓冲区大小,提高性能 buffer-size = 65536 # 确保当 uWSGI 进程收到 TERM 信号时退出(用于 Docker 停止容器) die-on-term = true
- Dockerfile
FROM python:3.9 LABEL author="name<******@qq.com>" WORKDIR /home/www/backend ADD . /home/www/backend RUN pip install -r requirements.txt \ -i https://pypi.tuna.tsinghua.edu.cn/simple \ &&pip install uwsgi \ -i https://pypi.tuna.tsinghua.edu.cn/simple ENTRYPOINT /usr/local/bin/uwsgi --ini /home/www/backend/uwsgi.ini
- 注释
Dockerfile中不可有注释 FROM:后接构建自定义镜像的模板镜像 LABEL:标签,key:value形式 WORKDIR:指定后续语句的工作目录,相当于cd命令,docker exec进入的也是此文件夹 ADD:拷贝宿主机文件到镜像,宿主机若为路径如:/home/www,则仅拷贝其中文件到 镜像文件夹内,优势是源文件是压缩文件,此命令会先解压再拷贝进镜像 RUN:构建镜像,在镜像内运行的命令,每个RUN在镜像中生成一层,效率与层数成反比, 用此种写法,仅生成一层,多个命令写法 ENTRYPOINT:使用此镜像,docker run命令生成容器,容器启动后第一个执行的语句
4.2 构建自定义镜像
- 命令
cd 父目录/project # -t后面接的是 镜像名:版本号,别落了最后的点儿 # 点号:是指镜像构建时打包上传到Docker引擎中的文件的目录 docker build -t django:v1.0 . # 查看镜像创建情况 docker images # 创建自定义镜像的容器:请求在端口流转,宿主2000=>docker中uwsgi的2048端口=> # 通过uwsgi配置文件传给main/wsgi.py,进而进入Django项目 docker run -id --name django_v1.0 -p 2000:2048 django:v1.0 # 可进入运行中的容器,退出的容器可以`docker log 容器序号`查看原因 docker exec -it django_v1.0 bash
五、容器集群管理
5.1 docker-compose概述及主配置
- 功能:用于管理多容器协同,包括存储、网络、启动调度等
- 测试环境:
- win10:Docker Desktop 4.8.0
- mac:Docker Desktop 4.8.0
- linux:
# 下载网址的docker-compose文件,并重命名拷贝到相应目录下 $ sudo curl -L "https://github.com/docker/compose/releases/download/v2.2.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose # 授予可执行权限 sudo chmod +x /usr/local/bin/docker-compose # 安装完成,查看版本 docker-compose version
语句意义:只是去上面的网址下载docker-compose这个文件,放到相应位置改名字改权限,也可以网页直接下载
- 总体文件布局
- docker-compose.yml(有另一版本6.2节)
# 服务:每一个对应一个容器 services: # 服务名:随便起,也为容器IP地址别名,即nginx对应172.3.2.1这样 nginx: # docker拉取镜像创建容器 image: nginx:1.20 # 启动的容器名:对应docker run --name 这项 container_name: nginx_main # 若关闭,自动重启,前提是docker守护进程在运行,即docker开着 restart: always # 端口暴露:对应docker run -p 这项 ports: - 80:80 - 443:443 # 给另一个服务的容器的IP设置别名,此处django对应 # 容器be_localip的IP,用一个yml文件生成的容器均在同 # 一个局域网中,单一网络中直接用服务名即可,此项较少用 links: - "be_localip:django" # 设置容器启动依赖关系:此项仅限制各服务的启动次序, # 不代表be_localip启动完成再启动nginx,若要完成后再启动 # 官网有wait-for的command的相关设置 depends_on: - be_localip # 见db处volumes注释 volumes: # vue打包项目,即index.html所在目录 # win10中测试相对路径可行,不用绝对路径写法 - ./0_frontend/html:/usr/share/nginx/html # ngingx配置目录、文件(虚拟机及通用配置) - ./0_frontend/conf.d:/etc/nginx/conf.d - ./0_frontend/nginx.conf:/etc/nginx/nginx.conf be_localip: # 此容器镜像通过./1_backend/localIPBE/Dockerfile创建 # win10中测试相对路径可行,不用绝对路径写法 build: ./1_backend/localIPBE container_name: BE_localIP restart: always # 此处只写一个,即指定宿主机任意端口绑定此容器的2048端口, # 实质是仅将此端口暴露给内部容器使用,不给宿主机用 ports: - 2048 depends_on: - db # 多后端写法示例,nginx配置文件做重定向此后端 # BE_doubi: # build: ./1_backend/doubi.com # container_name: BE_doubi # restart: always # ports: # - 2049 db: image: postgres:12.10 container_name: postgres_main restart: always ports: - 5432:5432 # 设定环境变量:数据库名、用户名、密码、时区, # 在Django的setting进行对应设置 environment: - POSTGRES_DB=postgres - POSTGRES_USER=postgres - POSTGRES_PASSWORD=postgres - TZ=Asia/Shanghai # 挂载卷: # 1、功能:将postgresql的数据卷挂载到宿主机对应目录下 # 2、docker命令:对应`docker run -v 宿主机目录:容器内目录` # 3、特性:默认宿主机目录(不管是否为空)覆盖(而非删除)容器目录;数据库镜像此 # 设置特殊,经测试宿主机为空目录,会被容器目录反向拷贝填满,但若宿主机目录非空,则 # 宿主机目录覆盖容器目录 # 4、数据持久性:两目录数据同步,停止甚至销毁容器,宿主目录不变,可多容器绑定 # 同一宿主目录 volumes: - ./2_database/postgres:/var/lib/postgresql/data
5.2 nginx及前端
- 修改前注意:chrome浏览器会强制把http转成https,如果测试,可以使用edge、safra浏览器
- 文件布局
nginx配置文件及性能优化详见:传送门,以下是精简版nginx配置
- nginx.conf
user nginx; worker_processes auto; error_log /var/log/nginx/error.log notice; pid /var/run/nginx.pid; events { worker_connections 1024; } http { include /etc/nginx/mime.types; default_type application/octet-stream; log_format main '$remote_addr - $remote_user [$time_local] "$request" ' '$status $body_bytes_sent "$http_referer" ' '"$http_user_agent" "$http_x_forwarded_for"'; access_log /var/log/nginx/access.log main; sendfile on; keepalive_timeout 65; include /etc/nginx/conf.d/*.conf; }
- default.conf(镜像默认的,处理http://localhost/请求的)
server { listen 80; listen [::]:80; server_name localhost; #access_log /var/log/nginx/host.access.log main; location / { root /usr/share/nginx/html; index index.html index.htm; } #error_page 404 /404.html; # 重定向服务器错误页到静态页面50x.html error_page 500 502 503 504 /50x.html; location = /50x.html { root /usr/share/nginx/html; } }
- localIP.conf(处理http://192.168.3.120/请求的)
server { listen 80; server_name 192.168.3.120; location / { root /usr/share/nginx/html/localIP/; # 解决刷新页面404问题,可以不加看效果 try_files $uri $uri/ /index.html; index index.html; } error_page 500 502 503 504 /50x.html; location = /50x.html { root /usr/share/nginx/html; } }
- doubi.com.conf (处理http://doubi.com、https://doubi.com请求的)
server { # 监听80和443端口,后面rewrite语句会强制将80转成443 listen 80; listen 443 ssl http2; server_name doubi.com; # 此处偷懒,都指向相同的vue前端,后续location中root在此后拼接 root /usr/share/nginx/html/doubi.com/; # 将404导给前端做路由跳转 # error_page 404 https://doubi.com/404.html;; # 强制http转https:rewrite语句 # permanent:浏览器地址栏显示重定向后的url if ($server_port !~ 443){ rewrite ^(/.*)$ https://$host$1 permanent; } # ssl密钥和证书:在注册域名处提取,并放到如下目录处 # centos中cert转pem命令如下: # openssl x509 -in doubi.com.crt -out doubi.com.pem -outform PEM ssl_certificate /etc/nginx/conf.d/certificate/doubi.com.pem; ssl_certificate_key /etc/nginx/conf.d/certificate/doubi.com.key; # ssl加密协议、算法 ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers EECDH+CHACHA20:EECDH+CHACHA20-draft:EECDH+AES128:RSA+AES128:EECDH+AES256:RSA+AES256:EECDH+3DES:RSA+3DES:!MD5; # 在使用SSLv3和TLS协议时,服务器加密算法将优于客户端加密算法 ssl_prefer_server_ciphers on; # 设置存储session参数的缓存的类型和大小:缓存在所有工作进程之间共享 ssl_session_cache shared:SSL:10m; # 设置存储session的缓存过期时间 ssl_session_timeout 10m; # 添加响应头:告诉浏览器只能通过HTTPS访问当前资源, 禁止HTTP方式 add_header Strict-Transport-Security "max-age=31536000"; #error_page 404 /404.html; error_page 500 502 503 504 /50x.html; #禁止访问的文件或目录 # location ~ ^/(\.user.ini|\.htaccess|\.git|\.svn|\.project|LICENSE|README.md) # { # return 404; # } # 设置浏览器上缓存图片资源过期时间 location ~ .*\.(gif|jpg|jpeg|png|bmp|swf)$ { expires 7d; error_log /dev/null; access_log /dev/null; } # 设置浏览器上js、css资源过期时间 location ~ .*\.(js|css)?$ { expires 12h; error_log /dev/null; access_log /dev/null; } # 设置后端:将请求转发给后端Django # 响应https://doubi.com/api/...请求 location /api/ { # 去掉URL中的/api/前缀 rewrite ^/api/(.*)$ /$1 break; # 将请求转发到本机2048号端口,对应yml的Django服务器 proxy_pass http://127.0.0.1:2048; proxy_set_header Host $host; # 将客户端的真实IP传递给uWSGI,后端可以用 # request.META.get('HTTP_X_FORWARDED_FOR')获得,做相应鉴权 proxy_set_header X-Real-IP $remote_addr; # 转发 X-Forwarded-For 头 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; # 转发请求协议(HTTP/HTTPS) proxy_set_header X-Forwarded-Proto $scheme; } # 见下效果:文件服务器,响应https://doubi.com/share/ location /share/ { root /usr/share/nginx; autoindex on; # on为比特单位,off为kb\mb单位 autoindex_exact_size off; # 显示文件修改时间 autoindex_localtime on; } # 响应https://doubi.com请求 location / { try_files $uri $uri/ /index.html; index index.html index.htm; } # main为格式:见nginx.conf access_log /var/log/nginx/doubi.com.log main; # info为错误等级 error_log /var/log/nginx/doubi.com.error.log info; }
本机测试doubi.com:需要在本机host文件(C:\Windows\System32\drivers\etc\hosts)增加一行
127.0.0.1 doubi.com
获取ssl密钥文件:腾讯管理的域名、阿里管理的域名- https://doubi.com/share/效果
- https://doubi.com/share/效果
5.3 后端
-
文件布局
-
django项目settings.py修改
""" ... # 开启生产环境 DEBUG = False ... # 开启访问白名单 ALLOWED_HOSTS = ['*'] ... # 此处设置postgresql参数 DATABASES = { 'default': { # 此模块要pip install psycopg2-binary 'ENGINE': 'django.db.backends.postgresql_psycopg2', # 以下与docker-compose.yml中配置相对应 'NAME': 'postgres', 'USER': 'postgres', 'PASSWORD': 'postgres', # 在yml文件中,db为数据库所在虚拟机的IP 'HOST': 'db', 'PORT': '5432', } } ... # 此处是关闭跨域访问限制 CORS_ALLOW_ALL_ORIGINS = True ...
-
Dockerfile
FROM python:3.9 LABEL author="name<******@qq.com>" WORKDIR /home/www/backend ADD . /home/www/backend RUN pip install -r requirements.txt \ -i https://pypi.tuna.tsinghua.edu.cn/simple \ &&pip install uwsgi \ -i https://pypi.tuna.tsinghua.edu.cn/simple ENTRYPOINT /usr/local/bin/uwsgi --ini /home/www/backend/uwsgi.ini
-
requirements.txt
# 注意补充这个连接postgresql数据库的模块 pip install psycopg2-binary pip freeze > requirements.txt
-
uwsgi.ini
[uwsgi] ########################需要修改######################## chdir = /home/www/backend module = main.wsgi:application http = 0.0.0.0:2048 ####################################################### processes = 2 threads = 2 master = true max-requests = 2000 harakiri = 30 vacuum = true thunder-lock = true logto = uwsgi.log log-date = true log-maxsize = 50000 log-reopen = true disable-logging = true pidfile = uwsgi.pid buffer-size = 65536 die-on-term = true
5.4 数据库
- 目录:./2_database/postgres会自动生成,用于后续挂载数据库中数据文件夹使用
5.5 命令
- 位置:docker-compose命令必须在此文件同级目录执行
- 命令
# 以下命令均需要在yml文件所在目录中运行 # 侦测yml文件中build项,镜像有更新需要主动运行此命令 docker-compose build # 首次运行会调用docker build和docker run针对每个容器 # -d:可选项,为后台运行,建议先不带看运行详情,没问题再-d docker-compose up [-d] # 停止并删除yml管理的容器,包含挂载的卷volume,不删镜像 docker-compose down # 启动、停止、重启服务容器集群 docker-compose start|stop|restart
- django数据库迁移
# 容器集群启动起来后需要做Django项目数据库迁移 docker exec -it BE_localIP bash > python manage.py makemigrations > python manage.py migrate > python manage.py createsuperuser
- debug用命令
# 查看挂载卷信息 docker volume ls # 删除挂载卷:卷标上条命令 docker volume rm 卷序号 # 打印容器输出日志:序号获取 docker ps -a docker logs 容器序号 # 查看容器详细信息 docker inspect 容器序号
六、常见问题
6.1 network host失效、完整权限Linux虚拟机
- 场景:
docker run -id --network host nginx:1.20
在win10和mac上失效 - 原因:host模式仅在Linux中有效,win10和mac上无此模块
- bridge与host网络
bridge模式:docker0相当于一个网桥,每个veth为一个路由器,docker0控制NAT端口映射和iptables规则,此模式下的相同网络名称的容器相当于在一个局域网中,由veth控制IP段,经过docker0,IP即被docker0和宿主机的iptables规则调整
host模式:容器直接使用宿主机的IP和端口,注意控制端口冲突,因为直接接触外网,性能更好
- 补充:构建完整权限Linux虚拟机
# 此命令会获取Linux完整权限,否则很多系统权限无法获得 docker run -itd --name centos7 --privileged --network host centos:7 init # 进入docker容器 docker exec -it centos7 bash # 安装systemctl yum install initscripts -y # 安装网络工具:如ping、ifconfig yum install net-tools -y # 安装ssh yum install openssh-server -y # 安装epel yum install epel-release
注意:通常不会单纯需要系统,会根据业务需要选择docker镜像,例如Django项目,运行环境是Python3.9,就下载此docker镜像,系统无关紧要,常规都是Ubuntu了,centos系列停止维护
6.2 后端获取客户端真实IP
- 前置:因6.1节原因,Nginx需要做黑名单、访问频率限制等获取客户端真实IP操作
- 思路:Nginx容器使用host模式,其他使用bridge模式
- docker-compose.yml修改
# 未注释的均为原样 services: nginx: image: nginx:1.20 container_name: nginx_main restart: always # 此处修改为host模式:与主机共用端口和IP # 此网络模式不与ports、links共存 network_mode: host depends_on: - be_localip volumes: - ./0_frontend/html:/usr/share/nginx/html - ./0_frontend/conf.d:/etc/nginx/conf.d - ./0_frontend/share:/usr/share/nginx/share - ./0_frontend/nginx.conf:/etc/nginx/nginx.conf be_localip: build: ./1_backend/localIPBE container_name: BE_localIP restart: always # 此处用bridge模式将端口暴露给主机 # 留给Nginx与后端uwsgi通信用:localhost:2048这样 ports: - 2048:2048 depends_on: - db