在咱们代码人的日常中,你一定见过以下对话:

"又出bug了?""对,但我这边是好的啊!""你先等等,我看看你的环境…""我这Node版本是16,MySQL是5.7,Redis…""我这全是最新版…""……"

现在,docker来解决烦恼了。

1. Docker基础命令

先记录最常用的命令。以后忘记了方便查询。

1.1 镜像相关命令

# 搜索镜像
docker search nginx

# 拉取镜像
docker pull nginx:latest  # 拉取最新版本
docker pull nginx:1.20.0  # 拉取特定版本
docker pull nginx@sha256:123...  # 通过sha256拉取

# 列出本地镜像
docker images
docker image ls

# 查看镜像详细信息
docker inspect nginx

# 删除镜像
docker rmi nginx  # 删除单个镜像
docker rmi $(docker images -q)  # 删除所有镜像
docker image prune  # 删除未使用的镜像

# 保存和加载镜像
docker save nginx > nginx.tar  # 导出镜像到文件
docker load < nginx.tar  # 从文件加载镜像

# 构建镜像
docker build -t myapp:1.0 .  # 从当前目录的Dockerfile构建
docker build -f my.dockerfile .  # 使用自定义Dockerfile


1.2 容器操作命令

# 创建并运行容器
docker run nginx  # 简单运行
docker run -d nginx  # 后台运行
docker run -it nginx bash  # 交互式运行
docker run --name my-nginx nginx  # 指定容器名称

# 常用运行参数
docker run \
  -d \  # 后台运行
  --name my-app \  # 容器名称
  -p 8080:80 \  # 端口映射
  -v /host/path:/container/path \  # 挂载卷
  -e ENV_VAR=value \  # 环境变量
  --network my-network \  # 指定网络
  --restart always \  # 重启策略
  nginx

# 容器生命周期管理
docker start my-nginx  # 启动容器
docker stop my-nginx   # 停止容器
docker restart my-nginx  # 重启容器
docker kill my-nginx  # 强制停止容器

# 查看容器
docker ps  # 查看运行中的容器
docker ps -a  # 查看所有容器
docker top my-nginx  # 查看容器进程
docker stats  # 查看容器资源使用情况

# 容器日志和信息
docker logs my-nginx  # 查看日志
docker logs -f my-nginx  # 实时查看日志
docker inspect my-nginx  # 查看容器详细信息

# 进入容器
docker exec -it my-nginx bash  # 在运行的容器中执行命令
docker attach my-nginx  # 连接到容器的主进程

# 文件操作
docker cp my-nginx:/etc/nginx/nginx.conf .  # 从容器复制文件
docker cp nginx.conf my-nginx:/etc/nginx/  # 复制文件到容器

# 删除容器
docker rm my-nginx  # 删除已停止的容器
docker rm -f my-nginx  # 强制删除运行中的容器
docker container prune  # 删除所有已停止的容器


1.3 网络管理命令

# 网络查看
docker network ls  # 列出网络
docker network inspect bridge  # 查看网络详情

# 网络创建
docker network create my-network  # 创建网络
docker network create \
  --driver overlay \
  --subnet 10.0.0.0/24 \
  --gateway 10.0.0.1 \
  my-network

# 网络连接
docker network connect my-network my-nginx  # 连接容器到网络
docker network disconnect my-network my-nginx  # 断开网络连接

# 网络清理
docker network prune  # 删除未使用的网络


1.4 数据卷命令

# 数据卷管理
docker volume create my-vol  # 创建数据卷
docker volume ls  # 列出数据卷
docker volume inspect my-vol  # 查看数据卷详情
docker volume rm my-vol  # 删除数据卷
docker volume prune  # 删除未使用的数据卷


1.5 系统和清理命令

# 系统信息
docker info  # 显示系统信息
docker version  # 显示版本信息
docker events  # 实时显示系统事件

# 清理命令
docker system df  # 显示磁盘使用情况
docker system prune  # 清理所有未使用的资源
docker system prune -a  # 清理所有未使用的资源(包括未使用的镜像)


2. 重新理解docker

2.1 Docker不是虚拟机?

我之前是把Docker当作轻量级虚拟机。实际上:

# 查看容器内进程
docker exec container_name ps aux
# 在宿主机上也能看到对应进程
ps aux | grep container_process

🤔可以想一想:为什么容器内进程在宿主机上可见?这说明了什么?

关键点:

  • Docker使用Linux的namespace机制实现隔离
  • 容器和宿主机共享内核
  • cgroups控制资源使用

这可以这么比喻:

虚拟机就像是在家里完整地建了一套新房子(包括地基、水电等基础设施),Docker 就是在已有的房子里隔出一个独立的房间,共用已有的水电等基础设施。

3.开始容器化

3.1 基础镜像的选择

常见基础镜像对比:

版本类型大小特点适用场景
latest~1GB完整工具链开发测试
slim~200MB精简依赖生产环境
alpine~50MB极度精简特定场景

docker pull node:16
docker pull node:16-slim
docker pull node:16-alpine


3.2 Dockerfile举例

就拿我搭建的一个java的Dockerfile为例:

FROM harbor.test.com/test/centos:latest
LABEL description="ruoyi-java-backed-server"
ENV JAVA_HOME=/apps/soft/jdk-17.0.13
ENV PATH=$JAVA_HOME/bin:$PATH
COPY CentOS-Base.repo /etc/yum.repos.d/
RUN cd /etc/yum.repos.d/ && \
    sed -i 's/mirrorlist/#mirrorlist/g' /etc/yum.repos.d/CentOS-* && \
    sed -i 's|#baseurl=http://mirror.centos.org|baseurl=http://vault.centos.org|g' /etc/yum.repos.d/CentOS-* && \
    yum clean all && \
    yum makecache
RUN mkdir -p /apps/soft \
    && mkdir -p /apps/code/ruoyi
RUN yum install -y fontconfig && \
    yum install -y dejavu-sans-fonts
ADD jdk-17.0.13.tar.gz /apps/soft
COPY ruoyi-admin.jar /apps/code/ruoyi
EXPOSE 8080
VOLUME /apps/code/ruoyi
CMD ["java","-jar","/apps/code/ruoyi/ruoyi-admin.jar"]

当然这是不够完美的,我专门找了一个完美的来对比
一个优化后的Node.js应用Dockerfile:

# 构建阶段
FROM node:16-slim as builder
WORKDIR /build
COPY package*.json ./
RUN npm ci --only=production
COPY . .
RUN npm run build

# 运行阶段
FROM node:16-slim
WORKDIR /app
COPY --from=builder /build/dist ./dist
COPY --from=builder /build/node_modules ./node_modules
COPY package*.json ./

USER node
EXPOSE 3000
CMD ["npm", "start"]

关键优化点:

  1. 多阶段构建减小最终镜像大小
  2. 合理的指令顺序利用缓存
  3. 使用非root用户运行
  4. 明确指定依赖版本

3.3 构建与运行

# 构建镜像
docker build -t myapp:1.0 .

# 运行容器
docker run -d \
  --name myapp \
  -p 3000:3000 \
  -v logs:/app/logs \
  --memory="512m" \
  --cpus="1.0" \
  myapp:1.0

当然还有更复杂的:

docker run -d   
 --name ruoyi-backed-server  
 -p 8080:8080  
 --link ruoyi-mysql-server:mysql-server   
 --link ruoyi-redis-server:redis-server   
 -v /usr/lib64:/usr/lib64:ro   
 -v /usr/share/fonts:/usr/share/fonts:ro   
 -e JAVA_OPTS="-Djava.awt.headless=true 
 -Dfile.encoding=UTF-8 -Dsun.jnu.encoding=UTF-8"   
 -e LANG=C.UTF-8   harbor.test.com:80/test/backed:java-v17.0.13


4. 生产环境实战

4.1 Docker Compose

完整的docker-compose.yml示例:

version: "3.3"
services:
  ruoyi-mysql-server:
    build:
      context: mysql/
      dockerfile: Dockerfile
    image: harbor.test.com/test/mysql:8.0.26
    container_name: ruoyi-mysql-container
    ports:
      - "3306:3306"
    environment:
      MYSQL_ROOT_PASSWORD: root
      TZ: "Asia/Shanghai"
    volumes:
      - /apps/ruoyi/mysql/my.cnf:/etc/mysql/my.cnf
      - /apps/ruoyi/mysql/conf.d:/etc/mysql/conf.d
      - /apps/ruoyi/mysql/data:/var/lib/mysql
      - /apps/ruoyi/mysql/logs:/var/log/mysql
    networks:
      default:
        aliases:
          - mysql-server

  ruoyi-redis-server:
    build:
      context: redis/
      dockerfile: Dockerfile
    image: harbor.test.com/test/redis:latest
    container_name: ruoyi-redis-container
    ports:
      - "6379:6379"
    volumes:
      - /apps/ruoyi/redis/data:/data
      - /apps/ruoyi/redis/redis.conf:/etc/redis/redis.conf
    networks:
      default:
        aliases:
          - redis-server

  ruoyi-backed-server:
    build:
      context: backend/
      dockerfile: Dockerfile
    image: harbor.test.com/test/centos:latest
    container_name: ruoyi-backed-container
    ports:
      - "8080:8080"
    volumes:
      - /apps/ruoyi/backend:/apps/code/ruoyi
    environment:
      - JAVA_OPTS="-Djava.awt.headless=true -Dfile.encoding=UTF-8 -Dsun.jnu.encoding=UTF-8"
      - LANG=C.UTF-8
    depends_on:
      - ruoyi-mysql-server
      - ruoyi-redis-server

  ruoyi-nginx-server:
    build:
      context: nginx/
      dockerfile: Dockerfile
    image: harbor.test.com/test/nginx:alpine
    container_name: ruoyi-nginx-container
    ports:
      - "80:80"
    volumes:
      - /apps/ruoyi/nginx/nginx.conf:/etc/nginx/nginx.conf
      - /apps/ruoyi/nginx/conf.d:/etc/nginx/conf.d
      - /apps/ruoyi/nginx/logs:/var/log/nginx
      - /apps/ruoyi/nginx/ruoyi-ui:/data
    depends_on:
      - ruoyi-backed-server


5. 常见问题

5.1 构建失败

常见原因和解决方案:

  1. 构建上下文过大
# 使用.dockerignore
node_modules
npm-debug.log
  1. 网络问题
# 使用镜像加速
docker build --network host .


5.2 容器启动失败

排查步骤:

# 查看容器状态
docker ps -a

# 查看启动日志
docker logs container_id

# 进入容器排查
docker exec -it container_id sh


5.3 性能问题

# 查看容器资源使用
docker stats

# 查看详细指标
docker inspect container_id


写在最后

又把命令敲了一遍,但是以后还是可能忘,还是要多练,多部署几个项目就熟了。

当然,命令不是最重要的,忘了的话查起来很快。关键是流程要知道。