
目录
[打包镜像(docker build)](#打包镜像(docker build))
[容器编排(docker compose)](#容器编排(docker compose))
操作
镜像操作:
docker images # 查看镜像 docker pull nginx # 拉取镜像 docker rmi nginx # 删除镜像 docker build -t my-app . # 构建镜像
容器操作:
docker ps # 查看容器 docker run nginx # 启动容器 docker stop 容器名 # 停止容器 docker start 容器名 # 启动容器 docker rm 容器名 # 删除容器 docker exec -it 容器名 bash # 进入容器
日志和监控
docker logs 容器名 # 查看日志 docker logs -f 容器名 # 实时日志 docker stats # 资源监控 docker inspect 容器名 # 查看详情
清理
docker container prune # 清理停止的容器 docker image prune # 清理悬空镜像 docker volume prune # 清理未使用的卷 docker system prune # 清理所有未使用的资源
启动
docker run 常用参数
参数 | 说明 | 示例 |
---|---|---|
-d |
后台运行 | docker run -d nginx |
-it |
交互模式 | docker run -it ubuntu bash |
-p |
端口映射 | -p 8080:80 |
-v |
挂载卷 | -v /host:/container |
-e |
环境变量 | -e KEY=value |
--name |
容器名称 | --name my-app |
--rm |
自动删除 | --rm |
--network |
网络设置 | --network my-net |
连接
docker exec 常用参数
参数 | 说明 | 示例 |
---|---|---|
-it |
交互模式 | docker exec -it 容器名 bash |
-u |
指定用户 | -u root |
-e |
环境变量 | -e VAR=value |
打包镜像(docker build)
docker使用docker file来将指定资源打包成镜像,docker提供了一套命令来编写docker file:
-
from,指定基础镜像
-
run,执行命令
-
copy,复制文件
-
add,添加文件
-
workdir,工作目录
-
expose,暴露端口
-
cmd和entrypoint,启动
from:
# 选择官方镜像(推荐) FROM node:18-alpine # 轻量级Node.js FROM python:3.11-slim # 轻量级Python FROM nginx:alpine # 轻量级Nginx # 指定版本(不要用latest) FROM ubuntu:20.04 # 明确版本号 FROM python:3.11.4-slim # 具体小版本
run:
# 单一命令 RUN apt-get update # 多个命令合并(减少镜像层数) RUN apt-get update && apt-get install -y \ curl \ git \ vim \ && rm -rf /var/lib/apt/lists/* # 清理缓存 # 安装Python包 RUN pip install --no-cache-dir flask gunicorn # 安装Node.js包 RUN npm install --production
COPY vs ADD:
# COPY - 简单复制(推荐) COPY package.json . # 复制单个文件 COPY src/ ./src/ # 复制整个目录 COPY --chown=user:group file.txt . # 复制并设置权限 # ADD - 有特殊功能(谨慎使用) ADD https://example.com/file.tar.gz /tmp/ # 可以下载URL ADD file.tar.gz /tmp/ # 会自动解压压缩包
workdir:
WORKDIR /app # 进入/app目录 RUN pwd # 现在在/app WORKDIR src # 进入/app/src RUN pwd # 现在在/app/src # 相当于: # cd /app # cd src
expose:
EXPOSE 80 # 暴露80端口(HTTP) EXPOSE 443 # 暴露443端口(HTTPS) EXPOSE 3000/tcp # 指定TCP协议 EXPOSE 3000/udp # 指定UDP协议 # 注意:这只是声明,实际映射要在 docker run 时用 -p 参数
CMD vs ENTRYPOINT:
# CMD - 默认启动命令(可以被覆盖) CMD ["nginx", "-g", "daemon off;"] CMD ["python", "app.py"] CMD ["npm", "start"] # ENTRYPOINT - 入口点(不容易被覆盖) ENTRYPOINT ["java", "-jar"] # 组合使用(常见用法) ENTRYPOINT ["java", "-jar"] CMD ["app.jar"] # 运行时可以覆盖:docker run my-app other.jar
下面是构建一个spring boot应用的docker file:
# 构建阶段 FROM maven:3.8.6-openjdk-17 AS builder WORKDIR /build # 先复制pom文件(利用缓存) COPY pom.xml . RUN mvn dependency:go-offline # 复制源代码 COPY src ./src # 打包应用 RUN mvn package -DskipTests # 运行阶段 FROM openjdk:17-jdk-slim WORKDIR /app # 创建非root用户 RUN groupadd -r spring && useradd -r -g spring spring USER spring # 复制打包好的jar文件 COPY --from=builder --chown=spring:spring /build/target/*.jar app.jar # 暴露端口 EXPOSE 8080 # 启动应用 ENTRYPOINT ["java", "-jar", "app.jar"]
打包指令:
docker build -t my-custom-nginx:1.0 . docker run --name energy-saving-platform -p 9999:9999 -d energy-saving-platform:1.0.0
网络
不同服务打包成不同镜像,启动成不同的容器,容器之间通过URL(IP:端口:接口地址)来访问,这样做没问题。但是IP和端口是可能会变动的,这种改动可能会引起我们需要重新去配置DockerFile,重新打包镜像。docker支持通过配置网络实现通过容器名来访问具体服务。所以在多容器通信的场景下,自定义docker网络会是明智之举。
docker支持以下网络模式:
-
bridge
-
不自定义网络时,会用一个默认的bridge,也可以自定义一个bridge
-
优点:简单易用,一个物理机端口能运行多个应用,端口资源占用不严重。
-
缺点:性能不高,毕竟请求要走好几层过滤。
-
-
host
-
直接使用物理机的端口
-
优点:性能高,毕竟请求不需要走好几层,适用于对请求响应速度有高要求的场景,比如物联网、高并发应用
-
缺点:不安全,一个物理机端口只能运行一个容器,端口资源占用严重。
-
-
none
-
相当于是个容器内网,容器内自己玩儿自己的,不能与外部网络通信。
-
优点缺点不展开,毕竟基本不会用到。
-
-
overlay
-
不同物理机的容器可以直接通信
-
优点:跨机器
-
缺点:需要Swarm或K8s集群,提升管理成本
-
配置示例
查看docker网络相关命令
# 查看所有网络 docker network ls # 查看网络详细信息 docker network inspect <network-name> # 查看容器网络配置 docker inspect <container-name> | grep -A 20 NetworkSettings # 测试容器间连通性 docker exec <container1> ping <container2> # 查看端口映射 docker port <container-name>
默认bridge
# 启动容器,使用默认bridge网络 docker run -d --name webapp1 -p 8080:80 nginx docker run -d --name webapp2 -p 8081:80 nginx # 查看网络信息 docker network inspect bridge # 测试连通性(需要通过IP访问) docker exec webapp1 curl http://172.17.0.3 # 需要知道webapp2的IP
自定义bridge
# 创建自定义bridge网络 docker network create my-bridge-network # 启动容器并加入自定义网络 docker run -d --name frontend --network my-bridge-network -p 80:80 nginx docker run -d --name backend --network my-bridge-network -p 3000:3000 node-app docker run -d --name database --network my-bridge-network -p 3306:3306 mysql:8.0 # 现在容器间可以通过名字访问! docker exec frontend curl http://backend:3000 docker exec backend ping database # 查看网络详情 docker network inspect my-bridge-network
带子网的自定义bridge
# 创建指定子网的bridge网络 docker network create \ --driver bridge \ --subnet 192.168.100.0/24 \ --gateway 192.168.100.1 \ my-custom-subnet # 启动容器并指定IP docker run -d --name app1 \ --network my-custom-subnet \ --ip 192.168.100.10 \ my-app:latest docker run -d --name app2 \ --network my-custom-subnet \ --ip 192.168.100.11 \ my-app:latest
host
# 使用host网络模式 docker run -d --name nginx-host --network host nginx # 此时nginx直接使用主机的80端口 # 访问: http://localhost:80 # 查看进程,可以看到直接使用主机网络 netstat -tlnp | grep :80
none就不展示了,没必要。
overlay需要依赖于docker swarm,docker swarm是一个docker官方提供的容器编排工具,允许将多个 Docker 主机组成一个集群,以集群的方式运行和管理容器。
由于技术选型上是用k8s来编排、管理容器集群,所以不展开docker swarm,只需要知道docker的容器编排有轻量级的swarm和重量级的k8s即可。
容器编排(docker compose)
为什么要容器编排
容器编排,即用配置文件编写好容器之间的依赖关系、启动顺序等。docker提供了docker compose来对容器进行编排,用docker-compose.yml来配置,进行容器编排。多镜像部署且需要通信需要写很多命令,假设一个eureka容器和一个应用容器。
没有 Docker Compose 时,两个问题:
-
整个过程中要手动执行docker build去重新构建镜像,还要去docker run重新拉起应用,还要来回切目录,很麻烦,而且还要顾及启动顺序,先启动注册中心,然后启动应用。命令又长又复杂。
-
IP地址或者端口变化时,要重新修改配置。
使用 Docker Compose 时,只需要:
-
在一个
docker-compose.yml
文件中定义这两个服务。执行一条命令:docker compose up
。避免了很多手动命令操作。 -
通过配置网络,支持服务之间可以通过名称来访问,规避了IP和端口变化带来的问题。
所有步骤都会自动完成。这极大地提高了开发和测试的效率。
没有docker compose的纯手动操作:




用容器的编排脚本,一个脚本就可以搞定,不用上面那么零碎的手动操作。
docker compose使用docker-compose.yml来编排容器,其中要声明和操作的无非就是:
-
服务 (Services):每个容器就是一个服务
-
项目 (Project):一组关联服务的集合
-
网络 (Networks):容器间的通信网络
-
数据卷 (Volumes):数据持久化
docker-compose.yml结构如下:
version: '3.8' # Compose 文件版本 services: # 定义所有服务 service1: # 服务1配置 service2: # 服务2配置 networks: # 自定义网络 volumes: # 数据卷定义
常用服务配置项
配置项 | 说明 | 示例 |
---|---|---|
image |
使用现有镜像 | image: nginx:alpine |
build |
从 Dockerfile 构建 | build: . |
ports |
端口映射 | ports: - "80:80" |
environment |
环境变量 | environment: - KEY=VALUE |
volumes |
数据卷挂载 | volumes: - ./data:/app/data |
networks |
加入网络 | networks: - app-network |
depends_on |
依赖关系 | depends_on: - db |
restart |
重启策略 | restart: always |
docker-compose.yml:
services: eureka-server: build: context: ./eurekaServer dockerfile: Dockerfile ports: - "9001:9001" environment: - JAVA_OPTS=-Xmx512m -Xms256m container_name: eureka-server networks: - microservice-net restart: unless-stopped # 订单服务 order-service: build: context: ./orderServer dockerfile: Dockerfile ports: - "9002:9002" environment: - JAVA_OPTS=-Xmx512m -Xms256m container_name: order-service networks: - microservice-net depends_on: - eureka-server restart: unless-stopped networks: microservice-net: driver: bridge
上面的脚本不能保证启动顺序,需要用健康检查来保证启动顺序:
services: eureka-server: build: context: ./eurekaServer dockerfile: Dockerfile ports: - "9001:9001" environment: - JAVA_OPTS=-Xmx512m -Xms256m container_name: eureka-server networks: - microservice-net healthcheck: test: ["CMD-SHELL", "timeout 5 bash -c 'cat < /dev/null > /dev/tcp/localhost/9001' || exit 1"] interval: 10s timeout: 5s retries: 18 # 总共等待 3 分钟 (10s * 18) start_period: 30s restart: unless-stopped order-service: build: context: ./orderServer dockerfile: Dockerfile ports: - "9002:9002" environment: - JAVA_OPTS=-Xmx512m -Xms256m - EUREKA_CLIENT_SERVICEURL_DEFAULTZONE=http://eureka-server:9001/eureka/ - EUREKA_CLIENT_INITIAL-INSTANCE-INFO-REPLICATION_INTERVAL_SECONDS=5 container_name: order-service networks: - microservice-net depends_on: eureka-server: condition: service_healthy # 关键:等待健康状态 restart: unless-stopped networks: microservice-net: driver: bridge
启动指令:
docker compose up -d ##如果不带-d就会是前台启动