Docker基础

什么是Docker

  • 先理解什么是jar包,java的jar包和docker的镜像比较类似,都是打包好的东西,可移植,区别就是jar包属于半成品,镜像是预制菜,加热下就能吃
  • 如果要在一台物理机上运行多个相同的服务,传统的方式是构建多个虚拟机来运行,但是这样特别占用物理资源,资源利用率不大,但是通过docker容器的方式,可以运行数百甚至数千的服务,并且Docker与宿主机系统同级,共用内核系统,运行的服务性能接近直接在物理机上运行

什么是镜像,什么是容器

按等级来说,镜像的等级比容器高,容器是镜像运行的产物,一个镜像可以运行多个容器。镜像就像模具,容器就是用这个模具生产出来的工具

docker镜像基础原理

  • 任何docker的应用镜像,本质上都依赖一个底层系统(基础镜像),只是这个底层系统是被精简过的,打包成了镜像格式,只提供应用运行必须的系统环境,应用镜像就是在这个地基(基础镜像)上,叠加了应用程序本身和她的依赖,最终形成一个可独立运行的单元
  • 基础镜像(精简版底层系统)+ 应用程序(如nginx)+ 应用依赖的分层文件集合 —- 基础镜像就是那个“被打包成镜像的精简底层系统”,只保留能执行系统服务的二进制文件,能读取文件,使用网络,链接依赖库等,是所有应用镜像的地基

如何构建最小化镜像

最小化镜像的「黄金法则」

  1. 基础镜像:优先 alpine/distroless/scratch,拒绝臃肿发行版。
  2. 依赖:构建时用 -dev 依赖,运行时只用运行时依赖,不装无用工具。
  3. 多阶段:构建和运行彻底分离,只复制最终产物。
  4. 清理:实时删除中间文件、缓存、符号表,不留冗余。
  5. 安全:用非 root 用户运行,减少攻击面。
  6. 减少分层

流程

  • 打包镜像image –> 移植到其他电脑 –> 运行镜像
  • image是分层的,优点是可复用且节约磁盘空间

镜像与容器相关命令

  • docker pull/push ubuntu:20.4 拉取/推送ubuntu镜像,后面是标签
  • docker build -t myapp:v1 . 构建镜像,后面的点不能省,代表当前目录
  • docker save -o myapp.tar myapp:v1 将镜像打包成tar包
  • docker load -i myapp.tar 加载tar包
  • docker cp /data myapp:/app 将宿主机目录/data 拷贝到镜像myapp的/app目录下,将两个路径互换就是反过来拷贝

  • docker rm -f ID/names 根据镜像id或镜像名删除镜像
  • docker images rm id/name 根据镜像id或镜像名删除镜像
  • docker system prune -a 清理docker中未被使用的资源
  • docker container prune 删除所有已停止的镜像

  • docker tag myapp:v2 修改镜像吗myapp的标签为v2
  • docker start/stop/kill/restart myapp 启动/停止/杀掉/重启 镜像

  • docker ps 查看正在运行的镜像,加-a的话可以列出所有镜像
  • docker images / docker image ls 列出所有镜像
  • docker logs -f mynewapp 根据镜像名/ID实时查看运行日志
  • docker inspect myapp 显示镜像详细信息
  • docker stats myapp 统计镜像资源使用

其他执行命令

  • docker run -d -p 8080:80 [-v /host/data:/app/data] [–name mynewapp] [-e ENV=prod] [–rm] nginx
  • -d为后台运行,-p端口映射,前面的8080是宿主机端口,用于用户访问,后面的80是镜像端口;-v是将linux的目录加到镜像目录中,
    –name是将本次运行的镜像命名,通过不同命名可以一个镜像跑多个,-e是指定环境变量,–rm是停止后自动删除镜像
  • docker -exec -it myapp /bin/bash 进入到镜像里面,这个/bin/bash是容器里面的shell,不是linux的,如果容器里面没有这个shell,那就不能用这个shell

docker网络

说明

docker网络中有三种模式,bridge桥接(对应vm的nat),host(与宿主机共用物理网卡),null 无网络

  • docker network create –driver=bridge [–subnet=192.168.10.0/24] mynet 创建bridge网络,可自定义私网,加入到创建的网络中的容易可以ping容器名
  • docker network create -d macvlan –subnet=192.168.146.0/24 –gateway=192.168.146.2 -o parent=ens33 myvlan 创建macvlan网络,macvlan网络,直接分配mac地址容器像物理设备一样出现在局域网中物理机也可以ping通容器

  • docker network rm mynet 根据网络名删除

  • docker network disconnect bridge myapp
  • docker network connect myapp

  • docker network ls 查看所有网络
  • docker inspect mysql8 | grep -E “NetworkMode|IPAddress” 查看容器名mysql8的网络信息

执行指令

  • docker run -d –network=bridge myapp 将镜像添加到bridge网络中

多阶段构建和一步构建

  • 多阶段构建的话再安装依赖等相关环境时,这些依赖只会再构建阶段,在运行时是不存在这些依赖的,大大减少了镜像大小
  • 一次性构建就会导致镜像文件包含依赖等相关环境,镜像文件变大

Dockerfile中的指令

在Dockerfile中,指令的大小写风格要一致

  • from 指定基础镜像,例:from nginx:alpine
  • run 执行命令(linux)命令
  • copy 复制文件/目录(主机复制到容器),例:copy ./app /usr/share/nginx/html 主机路径时根据docker build后面的路径来的
  • add 类似copy,支持自动解压和远程url,例:add https://example.com/file.tar.gz /data
  • cmd 容器启动时执行的命令(可被覆盖),CMD 另一个核心用法是给 ENTRYPOINT 提供默认参数
  • entrypoint 容器入口命令,不可被覆盖,必须被执行
  • env 设置环境变量(容器运行时使用)
  • arg 定义构建时的变量(构建时使用,构建完消失)
  • workdir 设置工作目录,后续命令的操作路径,相当于cd,例:workdir /app
  • user 指定执行命令的用户
  • volume 定义数据挂载点
  • label 添加元数据
  • healthcheck 定义容器健康检查,例:healthcheck –interval=30s cmd curl -f http://localhost/
  • onbuild 延迟执行命令(当本镜像作为其他镜像的基础时触发)
  • shell 覆盖默认的shell,例:shell[“/bin/bash”,”-c”]
  • stopsingal 设置停止容器信号

docker搭建Ruoyi前后端项目

创建自定义网络,让容器加入到一个镜像内

docker network create –driver=bridge [–subnet=192.168.1.0/24] ruoyi-net

mysql

  1. 拉取镜像docker pull mysql:8.0
  2. 运行镜像,docker run -d -p 3306:3306 –name mysql –network ruoyi-net -e MYSQL_ROOT_PASSWORD=123456 -e MYSQL_DATABASE=ruoyi-vue-pro -v /root/mysql-data:/var/lib/mysql –restart always mysql:8.0
  3. 进入数据库
    1
    2
    docker exec -it mysql bash #进入容器
    mysql -uroot -p
  4. 导入数据,有两种方法
    - 手动导入,将sql文件拷贝到容器中 docker cp /root/sql/quartz.sql mysql:/tmp,然后根据第三步进入容器,执行mysql -uroot -p ruoyi-vue-pro < quartz.sql, 将两个sql文件都导入进去
    - 自动导入,将数据库文件在docker run时-v挂到/docker-entrypoint-initdb.d下,容器初始化时会自动读取

redis

  1. 拉取镜像 docker pull redis:7.2
  2. 运行镜像
    docker run -d -p 6379:6379 --network ruoyi-net --name redis -v /root/redis-data:/data --restart always redis:7.2 redis-server --requirepass "redis123" --bind 0.0.0.0
  3. 进入redis
    1
    2
    docker exec -it redis bash #进入容器
    redis-cli

ruoyi-java

  1. 拉取源码,git clone https://gitee.com/zhijiantianya/ruoyi-vue-pro.git
  2. 修改application-local.yaml文件,将里面的mysql和redis写成容器名,不用写ip
  3. 编写Dockerfile文件
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    #第一阶段
    FROM maven:3.8.6-jdk-8 AS builder
    COPY settings.xml /root/.m2/settings.xml
    COPY ruoyi-vue-pro /ruoyi-vue-pro
    WORKDIR /ruoyi-vue-pro
    RUN mvn clean install package '-Dmaven.test.skip=true'

    #第二阶段:运行
    #FROM amazoncorretto:8-alpine
    FROM eclipse-temurin:8-jre
    RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
    WORKDIR /app
    COPY --from=builder /ruoyi-vue-pro/yudao-server/target/yudao-server.jar yudao-server.jar
    #JVM内存优化参数(根据实际情况调整)
    ENV JAVA_OPTS="-Xms256m -Xmx512m -XX:+UseG1GC"
    # 健康检查(Spring Boot Actuator 需配置)
    HEALTHCHECK --interval=30s --timeout=3s \
    CMD curl http://localhost:48080 || exit 1
    # 启动命令(支持传递额外参数)
    ENTRYPOINT exec java $JAVA_OPTS -jar yudao-server.jar $0 $@
  4. 创建settings.xml文件
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    <settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0
    https://maven.apache.org/xsd/settings-1.0.0.xsd">

    <mirrors>
    <mirror>
    <id>aliyunmaven</id>
    <mirrorOf>*</mirrorOf>
    <name>Aliyun Maven</name>
    <url>https://maven.aliyun.com/repository/public</url>
    </mirror>
    </mirrors>
    </settings>

  5. 记得将上面的源码,Dockerfile,xml文件放在同一个目录下,最好创一个目录放着三个
  6. 构建镜像 docker build -t –no-cache ruoyi-java:v1 .
  7. 运行镜像并查看日志 docker run -d -p 48080:48080 –network ruoyi-net –name ruoyi-java ruoyi-java:v1 && docker logs -f ruoyi-java

mini_nginx

  1. 这个是用来给前端运行的基础镜像
  2. 编写Dockerfile
    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
    from alpine:3.19 as builder

    run sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories && \
    apk add --no-cache \
    build-base \
    linux-headers \
    pcre-dev \
    zlib-dev \
    openssl-dev \
    wget \
    tar

    arg NGINX_VERSION=1.26.3
    run wget -q https://nginx.org/download/nginx-${NGINX_VERSION}.tar.gz && \
    tar -zxf nginx-${NGINX_VERSION}.tar.gz && \
    rm nginx-${NGINX_VERSION}.tar.gz

    workdir /nginx-${NGINX_VERSION}
    run ./configure \
    --prefix=/etc/nginx \
    --sbin-path=/usr/sbin/nginx \
    --modules-path=/usr/lib/nginx/modules \
    --conf-path=/etc/nginx/nginx.conf \
    --error-log-path=/var/log/nginx/error.log \
    --http-log-path=/var/log/nginx/access.log \
    --pid-path=/var/run/nginx.pid \
    --lock-path=/var/run/nginx.lock \
    --http-client-body-temp-path=/var/cache/nginx/client_temp \
    --http-proxy-temp-path=/var/cache/nginx/proxy_temp \
    --http-fastcgi-temp-path=/var/cache/nginx/fastcgi_temp \
    --http-uwsgi-temp-path=/var/cache/nginx/uwsgi_temp \
    --http-scgi-temp-path=/var/cache/nginx/scgi_temp \
    --user=nginx \
    --group=nginx \
    --with-compat \
    --with-threads \
    --with-http_ssl_module \
    --with-http_v2_module \
    --with-http_realip_module \
    --with-http_stub_status_module \
    --without-http_autoindex_module \
    --without-http_ssi_module \
    --without-mail_pop3_module \
    --without-mail_imap_module \
    --without-mail_smtp_module \
    --without-http_scgi_module \
    --without-http_uwsgi_module \
    --without-http_fastcgi_module \
    --with-cc-opt="-Os -fomit-frame-pointer -g" \
    --with-ld-opt="-Wl,--as-needed" && \
    make -j $(nproc) && make install && mkdir -p /usr/lib/nginx/modules && \
    strip /usr/sbin/nginx

    from alpine:3.19

    run sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories && \
    apk add --no-cache \
    pcre \
    zlib \
    openssl \
    tzdata && addgroup -S nginx && adduser -S -D -H -s /sbin/nologin -G nginx nginx && \
    mkdir -p /var/cache/nginx && \
    mkdir -p /var/log/nginx && \
    mkdir -p /usr/share/nginx/html && \
    echo 'My mini Nginx' > /usr/share/nginx/html/index.html && \
    chown -R nginx:nginx /var/cache/nginx && \
    chown -R nginx:nginx /var/log/nginx && \
    chown -R nginx:nginx /usr/share/nginx/html

    copy --from=builder /usr/sbin/nginx /usr/sbin/nginx
    copy --from=builder /etc/nginx /etc/nginx
    copy --from=builder /usr/lib/nginx/modules /usr/lib/nginx/modules

    copy nginx.conf /etc/nginx/nginx.conf
    #copy default.conf /etc/nginx/conf.d/default.conf
    copy yudao.conf /etc/nginx/conf.d/yudao.conf

    expose 80

    healthcheck --interval=30s --timeout=3s \
    cmd wget -q --spider http://localhost/ || exit 1

    cmd ["nginx", "-g" ,"daemon off;"]

  3. 编写default.conf nginx.conf
    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
    ### default.conf
    server {
    listen 80;
    server_name localhost;

    location / {
    root /usr/share/nginx/html;
    index index.html index.htm;
    }

    error_page 500 502 503 504 /50x.html;
    location = /50x.html {
    root /usr/share/nginx/html;
    }
    }

    ### nginx.conf
    ser nginx;
    worker_processes auto;

    error_log /var/log/nginx/error.log warn;
    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;
    server_tokens off;

    include /etc/nginx/conf.d/*.conf;
    }

  4. 构建镜像 docker build -t mini_nginx:v1 .

yudao前端

  1. 创建一个目录,用来放前端构建镜像的文件,进入目录
  2. 拉取源码 git clone https://gitee.com/yudaocode/yudao-ui-admin-vue3.git
  3. 修改那三个配置文件
  4. 编写Dockerfile文件
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    from node:20-alpine as build
    workdir /app

    copy yudao-ui-admin-vue3 ./

    run npm config set registry https://registry.npmmirror.com/ \
    && npm install -g pnpm@8.15.5 \
    && pnpm install

    run npm run build:prod

    from mini_nginx:v1

    run rm -rf /var/www/*

    copy --from=build /app/dist-prod /var/www/dist-prod

    copy ruoyi.conf /etc/nginx/conf.d/default.conf

    expose 80
    cmd ["nginx", "-g","daemon off;"]

  5. 编写nginx配置文件
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    server {
    listen 80;
    charset utf-8;
    server_name _;

    location / {
    root /var/www/dist-prod;
    index index.html index.hml;
    }

    location /login?redirect=/index {
    root /var/www/dis-prod;
    index index.html index.hml;
    }

    location /prod-api/ {
    proxy_set_header Host $http_host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header REMOTE-HOST $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_pass http://192.168.146.139:48080地方/; #这里面的ip可以写成容器名
    }
    }

  6. 构建镜像 docker build -t yudao:v1 .
  7. 运行镜像 docker run -d -p 80:80 –network ruoyi-net –name yudao yudao:v1