Docker 入门教程

olinonee大约 27 分钟deploy-operation使用指南docker

一、docker 简介

1.什么是 docker

Docker 是一个开源的应用容器引擎,由于 docker 影响巨大,今天也用 "Docker" 指代容器化技术。

2.docker 的优势

一键部署,开箱即用

容器使用基于 image 镜像的部署模式,image 中包含了运行应用程序所需的一切:代码、运行时、系统工具、系统库和配置文件。

无论是单个程序还是多个程序组成的复杂服务,或者分布式系统,都可以使用 docker rundocker compose up 命令一键部署,省去了大量搭建、配置环境、调试和排查错误的时间。

一次打包,到处运行

Docker 为容器创建了行业标准,使容器成为了软件交付过程中的一种标准化格式,将软打包成容器镜 (image),能够使软件在不同环境下运行一致,应用程序可以快速可靠地从一个环境移植到另外一个环境,并确保在所有的部署目标(例如开发、测试、生产环境)上都按预期运行,从而避免了 “在我电脑上是好的,怎么到你那却不能用了?” 的问题。

3.容器与虚拟机

容器包括应用程序及其所有依赖项。容器运行时,与宿主机共享操作系统内核,容器在 Linux 内核层面(使用 Cgroupsopen in new windownamespacesopen in new window)来实现进程间的隔离,容器在主机操作系统上的用户空间中作为独立进程运行。

因此,容器相比于虚拟机更加轻量化,它体积小,启动快,占用资源少,性能好。

虚拟机使用虚拟化技术,应用程序运行在完整的操作系统(OS)之上,因此占用的资源更多,安装更复杂。

但是由于容器与宿主机共享内核,所以在隔离性和安全性方面不如虚拟机。

容器和虚拟机区别
容器和虚拟机区别

4.参考文档

二、安装 docker

1.Windows 下安装 Docker

注意事项

容器主要使用 Linux 内核技术,因此 windows 下安装 docker 可能会有遇到各种问题,建议刚入门的同学先在 Linux 虚拟机里安装 docker,学完之后在 windows 下安装,免得遇到问题后,还没入门就放弃了。

2.Linux 下安装 Docker

安装环境:CentOS 7.3+

如果之前安装了旧版 docker,请先删除。

# 移除自带的 docker
sudo yum remove docker \
                  docker-client \
                  docker-client-latest \
                  docker-common \
                  docker-latest \
                  docker-latest-logrotate \
                  docker-logrotate \
                  docker-engine

安装仓库

## 安装 yum 工具包
sudo yum install -y yum-utils

## 添加 docker 仓库
sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo

安装 docker engine

sudo yum install docker-ce docker-ce-cli containerd.io docker-compose-plugin

说明

docker-ce:服务端
docker-ce-cli:客户端
客户端发出的指令交给服务端,服务端并不直接创建容器,而是交给 containerd.in 创建容器,销毁运行容器
docker-compose-plugin:容器编排工具

启动 docker,运行 hello world 查看是否成功

sudo systemctl start docker
sudo docker run hello-world

配置国内镜像仓库地址

新建 /etc/docker/daemon.json 文件,输入如下内容:

{
  "registry-mirrors": [
    "https://registry.docker-cn.com",
    "https://hub-mirror.c.163.com",
    "https://fsp2sfpr.mirror.aliyuncs.com/"
  ]
}

然后重启,配置开机启动

sudo systemctl restart docker
sudo systemctl enable docker
sudo systemctl enable containerd

三、docker run 开箱即用

1.docker 架构

Docker 架构
Docker 架构

registry 镜像仓库

registry 可以理解为镜像仓库,用于保存 docker image。

Docker Hub 是 docker 官方的镜像仓库,docker 命令默认从 docker hub 中拉取镜像。我们也可以搭建自己的镜像仓库。

image 镜像

image 可以理解为一个只读的应用模板。image 包含了应用程序及其所需要的依赖环境,例如可执行文件、环境变量、初始化脚本、启动命令等。

container 容器

容器是 image 的一个运行实例。当我们运行一个 image,就创建了一个容器。

2.docker pull 拉取镜像

从镜像仓库拉取镜像到本地

docker pull nginx 不写默认是 latest

docker pull nginx:latest

docker pull nginx:1.22

docker pull nginx:1.22.0-alpine

一般不建议使用 latest,因为最新的镜像是滚动更新的,过一段时间,可能跟你本地的不是同一个。

使用 docker images 命令查看本地镜像

3.docker run 命令

格式:docker run [可选参数] 镜像名:版本 []

公开端口 (-p)

docker run --name some-nginx -d -p 8080:80 nginx:1.22

默认情况下,容器无法通过外部网络访问。

需要使用 -p 参数将容器的端口映射到宿主机端口,才可以通过宿主机 IP 进行访问。

浏览器打开 http://宿主机IP:8080

Nginx
Nginx

-p 8080-8090:8080-8090 公开端口范围,前后必须对应

-p 192.168.56.106:8080:80 如果宿主机有多个 ip,可以指定绑定到哪个 ip

后台运行

docker run --name db-mysql -e MYSQL_ROOT_PASSWORD=123456 -d mysql:5.7

使用 run 命令,部署 mysql,docker 先去本地查找镜像,如果找不到,就去 docker hub 中拉取镜像

  • --name:定义容器的名称
  • -e:声明环境变量
  • -d:容器在后台运行

查看容器 ip

docker inspect db-mysql

可以使用以下命令操作容器:

## 启动 db-mysql 容器
docker start db-mysql
## 关闭容器
docker stop db-mysql
## 重启容器
docker restart db-mysql
## 删除容器
docker rm db-mysql

前台交互运行

## 创建一个新的容器,使用 mysql 客户端

docker run -it --rm mysql:5.7 mysql -h127.0.0.1 -uroot -p

说明

-it:使用交互模式,可以在控制台里输入、输出
--rm:在容器退出时自动删除容器。一般在使用客户端程序时使用此参数。如果每次使用客户端都创建一个新的容器,这样将占用大量的系统空间。
mysql -h127.0.0.1 -uroot -p:表示启动容器时执行的命令。

## 进入 db-mysql 容器中执行命令
docker exec -it db-mysql /bin/bash

说明

docker exec:在运行的容器中执行命令,一般配合 -it 参数使用交互模式

3.常用命令

## 查看正在运行的容器
docker ps
## 查看所有容器,包括正在运行和停止的
docker ps -a 
## 查看指定容器的信息
docker inspect [容器名或者容器 ID]
## 查看指定容器日志
docker logs [容器名或者容器 ID]
## 在容器和宿主机间复制文件

## 从宿主机复制到容器中
docker cp ./some_file 容器名:/work
## 从容器中复制到宿主机
docker cp 容器名:/var/logs/ /tmp/app_logs

4.参考文档

四、docker 网络

1.默认网络

docker 会自动创建三个网络,bridge , host, none

## 查看创建的网络
docker network ls
查看 docker 网络
查看 docker 网络
  • bridge

如果不指定,新创建的容器默认将连接到 bridge(桥接网络) 网络。

默认情况下,使用 bridge 网络,宿主机可以 ping 通容器 IP,容器中也能 ping 通宿主机。

容器之间只能通过 IP 地址相互访问,由于容器的 IP 会随着启动顺序发生变化,因此不推荐使用 IP 访问。

  • host

说明

慎用,可能会有安全问题。

容器与宿主机共享网络,不需要映射端口即可通过宿主机 IP 访问。(-p 选项会被忽略)

主机模式网络可用于优化性能,在容器需要处理大量端口的情况下,它不需要网络地址转换(NAT),并且不会为每个端口创建 “用户空间代理”。

  • none

禁用容器中所用网络,在启动容器时使用。

2.用户自定义网络

## 创建用户自定义网络
docker network create my-net

## 将已有容器连接到此网络
docker network connect my-net db-mysql

## 创建容器时指定网络
docker run -it --rm --network my-net mysql:5.7 mysql -hdb-mysql -uroot -p

在用户自定义网络上,容器之间可以通过容器名进行访问。

用户自定义网络使用 Docker 的嵌入式 DNS 服务器将容器名解析成 IP。

3.主机名解析

hostname

容器的 hostname 默认为容器的 ID。

## 启动容器,使用指定 hostname
docker run -it -d --hostname my-alpine --name my-alpine  alpine:3.15

## 查看容器信息
docker inspect --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' my-alpine
查看容器信息
查看容器信息

/etc/hosts

在容器内手动修改 /etc/hosts 文件,容器重启后会恢复默认配置。

要使 /etc/hosts 修改生效,使用 --add-host

docker run --add-host=my-alpine:172.17.0.3 -it --rm alpine:3.15

4.参考文档

五、docker 存储

将数据存储在容器中,一旦容器被删除,数据也会被删除。同时也会使容器变得越来越大,不方便恢复和迁移。

将数据存储到容器之外,这样删除容器也不会丢失数据。一旦容器故障,我们可以重新创建一个容器,将数据挂载到容器里,就可以快速的恢复。

1.存储方式

docker 提供了以下存储选项

Docker 存储方式
Docker 存储方式
  • volume 卷

卷存储在主机文件系统分配一块专有存储区域,由 Docker(在 Linux 上)管理,并且与主机的核心功能隔离。非 Docker 进程不能修改文件系统的这一部分。卷是在 Docker 中持久保存数据的最佳方式。

  • bind mount 绑定挂载

绑定挂载可以将主机文件系统上目录或文件装载到容器中,但是主机上的非 Docker 进程可以修改它们,同时在容器中也可以更改主机文件系统,包括创建、修改或删除文件或目录,使用不当,可能会带来安全隐患。

  • tmpfs 临时挂载

tmpfs 挂载仅存储在主机系统的内存中,从不写入主机系统的文件系统。当容器停止时,数据将被删除。

绑定挂载(bind mount)

绑定挂载适用以下场景:

  • 将配置文件从主机共享到容器。
  • 在 Docker 主机上的开发环境和容器之间共享源代码或编译目录。
    • 例如,可以将 Maven 的 target/ 目录挂载到容器中,每次在主机上用 Maven 打包项目时,容器内都可以使用新编译的程序包。
-v

绑定挂载将主机上的目录或者文件装载到容器中。绑定挂载会覆盖容器中的目录或文件。

如果宿主机目录不存在,docker 会自动创建这个目录。但是 docker 只自动创建文件夹,不会创建文件。

例如,mysql 的配置文件和数据存储目录使用主机的目录。可以将配置文件设置为只读(read-only)防止容器更改主机中的文件。

docker run -e MYSQL_ROOT_PASSWORD=123456 \
           -v /home/mysql/mysql.cnf:/etc/mysql/conf.d/mysql.cnf:ro  \
           -v /home/mysql/data:/var/lib/mysql  \
           -d mysql:5.7 
--tmpfs 临时挂载

临时挂载将数据保留在主机内存中,当容器停止时,文件将被删除。

docker run -d -it --tmpfs /tmp nginx:1.22-alpine

volume 卷

卷是 docker 容器存储数据的首选方式,卷有以下优势:

  • 卷可以在多个正在运行的容器之间共享数据。仅当显式删除卷时,才会删除卷。
  • 当你想要将容器数据存储在外部网络存储上或云提供商上,而不是本地时。
  • 卷更容易备份或迁移,当您需要备份、还原数据或将数据从一个 Docker 主机迁移到另一个 Docker 主机时,卷是更好的选择。

创建和挂载卷

## 创建挂载卷
docker volume create my-data

## 运行容器
docker run -e MYSQL_ROOT_PASSWORD=123456 \
           -v /home/mysql/conf.d/my.cnf:/etc/mysql/conf.d/my.cnf:ro  \
           -v my-data:/var/lib/mysql  \
           -d mysql:5.7 

创建 nfs 卷

docker volume create --driver local \
    --opt type=nfs \
    --opt o=addr=192.168.1.1,rw \
    --opt device=:/path/to/dir \
    vol-nfs

2.参考文档

六、部署自己的应用

本例子我们使用 docker 来部署一个应用系统,RuoYi 是一款用 java 编写的,基于 SpringBoot+Bootstrap 的后台管理系统。

RuoYi 官方文档:http://doc.ruoyi.vip/ruoyi/open in new window

源码下载:https://gitee.com/y_project/RuoYi/tree/v4.7.4/open in new window

将源码编译打包成 ruoyi-admin.jar 文件,放到宿主机 /home/app 目录下,/home/app/sql 目录下是数据库初始化脚本。

配置文件中修改了端口、数据库连接信息。

# application.yml
server:
  # 服务器的 HTTP 端口,默认为 80
  port: 8080

---
# application-druid.yml
  url: jdbc:mysql://ruoyi-db:3306/ry?useUnicode=true&characterEncoding=utf8
  username: root
  password: 123456

准备工作

## 创建存储卷
docker volume create ruoyi-data
## 创建网络
docker network create ruoyi-net

1.部署 mysql 并初始化数据库

我们在创建数据库容器的时候,需要做三件事:

  • 创建数据库 ry
  • 设置字符集为 utf-8
  • 执行数据库初始化脚本

使用 MYSQL_DATABASE 环境变量创建数据库

设置字符集 --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci

容器使用 /docker-entrypoint-initdb.d 目录下的脚本初始化数据库,脚本可以是 .sh.sql.sql.gz 这三种格式。

docker run -e MYSQL_ROOT_PASSWORD=123456 \
-e MYSQL_DATABASE=ry \
-v /home/app/sql:/docker-entrypoint-initdb.d \
-v ruoyi-data:/var/lib/mysql  \
--network ruoyi-net \
--name ruoyi-db \
-d mysql:5.7 \
--character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci

2.部署应用

docker run -p 8080:8080 \
-v /home/app/ruoyi-admin.jar:/usr/local/src/ruoyi-admin.jar \
--network ruoyi-net \
--name ruoyi-java \
-d openjdk:8u342-jre \
java -jar /usr/local/src/ruoyi-admin.jar

3.解决乱码问题

乱码问题是容器中 mysql 默认字符集引起的,我们需要将默认字符集改为 utf8mb4。

参考:https://github.com/docker-library/mysql/issues/131open in new window

可以进入容器,使用以下命令查看数据库字符集

## 进入容器
docker exec -it ruoyi-db mysql -uroot -p

> show variables like '%character%';

注意

由于删除容器不会删除存储卷,修改字符集需要删除存储卷,不然已经导入的数据字符集不会发生改变

删除容器和卷

docker stop ruoyi-db
docker rm ruoyi-db
docker volume rm ruoyi-data

可以通过以下两种解决方法

修改运行参数

使用环境变量 LANG=C.UTF-8 设置客户端字符集

docker run -e MYSQL_ROOT_PASSWORD=123456 \
-e MYSQL_DATABASE=ry \
-e LANG=C.UTF-8 \
-v /home/app/sql:/docker-entrypoint-initdb.d \
-v ruoyi-data:/var/lib/mysql  \
--network ruoyi-net \
--name ruoyi-db \
-d mysql:5.7 \
--character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci

或者

使用 --skip-character-set-client-handshake 忽略客户端字符集,使用客户端和服务端字符集一致。

docker run  -e MYSQL_ROOT_PASSWORD=123456 \
-e MYSQL_DATABASE=ry \
-v /home/app/sql:/docker-entrypoint-initdb.d \
-v ruoyi-data:/var/lib/mysql  \
--network ruoyi-net \
--name ruoyi-db \
-d mysql:5.7 \
--skip-character-set-client-handshake --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci 

修改配置文件

修改 /home/mysql/mysql.cnf

[mysqld]
character-set-server=utf8mb4
collation-server=utf8mb4_general_ci
init-connect='SET NAMES utf8mb4'

[client]
default-character-set=utf8mb4

[mysql]
default-character-set=utf8mb4

将配置文件挂载到容器中

docker run -e MYSQL_ROOT_PASSWORD=123456 \
-e MYSQL_DATABASE=ry \
-v /home/mysql/mysql.cnf:/etc/mysql/conf.d/mysql.cnf:ro  \
-v /home/app/sql:/docker-entrypoint-initdb.d \
-v ruoyi-data:/var/lib/mysql  \
--network ruoyi-net \
--name ruoyi-db \
-d mysql:5.7 

4.参考文档

七、docker compose 容器编排

在实际工作中,部署一个应用可能需要部署多个容器,一个一个部署非常不方便。docker compose 可以一键部署和启动多个容器,它使用 yaml 文件来编排服务。

github 和 docker hub 很多项目都提供了 docker-compose.yaml 文件,我们可以一键部署项目,非常方便。

1.一键部署 wordpress

wordpressopen in new window 是一个著名的开源博客系统。

将以下内容保存到本地的 docker-compose.yml 文件中。

docker compose 命令启动时,默认在当前目录下寻找 compose.yamlcompose.yml,为了兼容之前的版本,也会查找 docker-compose.yamldocker-compose.yml

也可以使用 -f 参数手动指定文件 docker compose -f docker-compose-dev.yml up -d

version: '3.1'

services:
  wordpress:
    image: wordpress
    restart: always
    ports:
      - 8080:80
    environment:
      WORDPRESS_DB_HOST: db
      WORDPRESS_DB_USER: exampleuser
      WORDPRESS_DB_PASSWORD: examplepass
      WORDPRESS_DB_NAME: exampledb
    volumes:
      - wordpress:/var/www/html

  db:
    image: mysql:5.7
    restart: always
    environment:
      MYSQL_DATABASE: exampledb
      MYSQL_USER: exampleuser
      MYSQL_PASSWORD: examplepass
      MYSQL_RANDOM_ROOT_PASSWORD: '1'
    volumes:
      - db:/var/lib/mysql

volumes:
  wordpress:
  db:
## 一键部署启动
docker compose up -d
## 启动/停止服务
docker compose start/stop 
## 停止并删除容器,不会删除存储卷 volume
docker compose down 

2.compose 文件结构

docker-compose.yml 通常需要包含以下几个顶级元素:

  • version 已弃用,早期版本需要此元素。
  • services 必要元素,定义一个或多个容器的运行参数,在 services 中可以通过以下元素定义容器的运行参数
    • image 容器镜像
    • ports 端口映射
    • environment 环境变量
    • networks 容器使用的网络
    • volumes 容器挂载的存储卷
    • command 容器启动时执行的命令
    • depends_on 定义启动顺序,复数形式(例如 ports,networks,volumes,depends_on)参数需要传入列表
  • networks 创建自定义网络
  • volumes 创建存储卷

3.yaml 文件语法

  • 缩进代表上下级关系
  • 缩进时不允许使用 Tab 键,只允许使用空格
  • : 键值对,后面必须有空格
  • - 列表,后面必须有空格
  • [ ] 数组
  • # 注释
  • {key:value, k1:v1} 键值对
  • | 多行文本块 如果一个文件中包含多个文档
  • --- 表示一个文档的开始

还有一种常见的用法

把公共的配置提取出来,用 & 来建立锚点,<< 合并到当前数据,用 * 引用锚点,例如:

version: '3.7'

# Settings and configurations that are common for all containers
x-minio-common: &minio-common
  image: quay.io/minio/minio:RELEASE.2022-08-13T21-54-44Z
  command: server --console-address ":9001" http://minio{1...2}/data{1...2}
  expose:
    - "9000"
    - "9001"
  
services:
  minio1:
    <<: *minio-common
    volumes:
      - data1-1:/data1
      - data1-2:/data2

  minio2:
    <<: *minio-common
    volumes:
      - data2-1:/data1
      - data2-2:/data2

volumes:
  data1-1:
  data1-2:
  data2-1:
  data2-2:

4.编排自己的项目

以 ruoyi 项目为例子,先采用挂载目录的方式部署应用,等我们学完 dockerfile 打包,就可以完整的部署应用了。

version: '3.1'

services:    
  ruoyi-app:
    #  docker run --name ruoyi-app      \
    #             -p 8080:8080        \
    #             --network ruoyi-net      \
    #             -v /home/app/ruoyi-admin.jar:/usr/local/src/ruoyi-admin.jar   \
    #             -d openjdk:8u342-jre    \
    #             java -jar /usr/local/src/ruoyi-admin.jar
    image: openjdk:8u342-jre
    ports:
      - 8080:8080
    volumes:
      - /home/app/ruoyi-admin.jar:/usr/local/src/ruoyi-admin.jar
    command: java -jar /usr/local/src/ruoyi-admin.jar
    networks:
      - ruoyi-net
    depends_on:
      - ruoyi-db
  
  ruoyi-db:
    #  docker run --name ruoyi-db -p 3303:3306 \
    #             --network ruoyi-net        \
    #             -v ruoyi-data:/var/lib/mysql  \
    #             -v /home/app/sql:/docker-entrypoint-initdb.d   \
    #             -e MYSQL_DATABASE=ry         \
    #             -e MYSQL_ROOT_PASSWORD=123456    \
    #             -d mysql:5.7      \
    #             --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci --skip-character-set-client-handshake
    image: mysql:5.7
    environment: 
      - MYSQL_ROOT_PASSWORD=123456
      - MYSQL_DATABASE=ry
    command: [
      "--character-set-server=utf8mb4",
      "--collation-server=utf8mb4_general_ci",
      "--skip-character-set-client-handshake"
      ]
    volumes:
      - /home/app/sql:/docker-entrypoint-initdb.d
      - ruoyi-data:/var/lib/mysql
    networks:
      - ruoyi-net

volumes:
  ruoyi-data:

networks:
  ruoyi-net:

command 支持以下写法:

# 推荐使用数组或列表的方式
# 数组
command:
  ["java",
   "-jar",
   "/usr/local/src/ruoyi-admin.jar"
  ]
# 列表
command:
  - java
    - -jar
    - /usr/local/src/ruoyi-admin.jar

# shell命令模式
command: java -jar /usr/local/src/ruoyi-admin.jar

执行复杂的脚本

command:
  - bash
  - "-c"
  - |
    set -ex
    # Generate mysql server-id from pod ordinal index.
    [[ `hostname` =~ -([0-9]+)$ ]] || exit 1
    ordinal=${BASH_REMATCH[1]}
    echo [mysqld] > /mnt/conf.d/server-id.cnf
    # Add an offset to avoid reserved server-id=0 value.
    echo server-id=$((100 + $ordinal)) >> /mnt/conf.d/server-id.cnf
    # Copy appropriate conf.d files from config-map to emptyDir.
    if [[ $ordinal -eq 0 ]]; then
      cp /mnt/config-map/primary.cnf /mnt/conf.d/
    else
      cp /mnt/config-map/replica.cnf /mnt/conf.d/
    fi

environment 支持如下两种写法

# 使用map
environment:
    MYSQL_DATABASE: exampledb
    MYSQL_USER: exampleuser
    MYSQL_PASSWORD: examplepass
    MYSQL_RANDOM_ROOT_PASSWORD: '1'

#使用列表
environment:
    - MYSQL_ROOT_PASSWORD=123456
    - MYSQL_DATABASE=ry
    - LANG=C.UTF-8

5.容器启动顺序 depends_on

数据库初始化完成之前,不会建立 connections。

MySQL 初始化
MySQL 初始化

depends_on 只能保证容器的启动和销毁顺序,不能确保依赖的容器是否 ready。

version: "3.9"
services:
  web:
    build: .
    depends_on:
      - db
      - redis
  redis:
    image: redis
  db:
    image: postgres

在这个例子中,depends_on 只能保证 web 容器在 dbredis 之后启动,不会关注他们的状态是否启动完成或准备就绪。

要确保应用服务在数据库初始化完成后再启动,需要配合 conditionopen in new windowhealthcheckopen in new window 使用。

services:
  web:
    build: .
    depends_on:
      db:
        condition: service_healthy
      redis:
        condition: service_started
  redis:
    image: redis
  db:
    image: postgres

condition 有三种状态:

  • service_started:容器已启动
  • service_healthy:容器处于健康状态
  • service_completed_successfully:容器执行完成且成功退出(退出状态码为 0)

我们来改造一下我们自己的 docker-compose.yaml 文件,完整例子如下:

services: 

  ruoyi-app:
    #  docker run --name ruoyi-app      \
    #             -p 8080:8080        \
    #             --network ruoyi-net      \
    #             -v /home/app/ruoyi-admin.jar:/usr/local/src/ruoyi-admin.jar   \
    #             -d openjdk:8u342-jre    \
    #             java -jar /usr/local/src/ruoyi-admin.jar
    image: openjdk:8u342-jre
    restart: always
    ports:
      - 8080:8080
    networks:
      - ruoyi-net
    volumes:
      - /home/app/ruoyi-admin.jar:/usr/local/src/ruoyi-admin.jar
    command: [ "java", "-jar", "/usr/local/src/ruoyi-admin.jar" ]
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:8080"]
      interval: 10s
      timeout: 5s
      retries: 5
      start_period: 10s
    depends_on:
      ruoyi-db:
        condition: service_healthy

  ruoyi-db:
    #  docker run --name ruoyi-db -p 3303:3306 \
    #             --network ruoyi-net        \
    #             -v ruoyi-data:/var/lib/mysql  \
    #             -v /home/app/sql:/docker-entrypoint-initdb.d   \
    #             -e MYSQL_DATABASE=ry         \
    #             -e MYSQL_ROOT_PASSWORD=123456    \
    #             -d mysql:5.7      \
    #             --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci --skip-character-set-client-handshake
    image: mysql:5.7
    environment:
      - MYSQL_DATABASE=ry
      - MYSQL_ROOT_PASSWORD=123456
    volumes:
      - ruoyi-data:/var/lib/mysql
      - /home/app/sql:/docker-entrypoint-initdb.d
    networks:
      - ruoyi-net
    command:
      [
        "--character-set-server=utf8mb4",
        "--collation-server=utf8mb4_unicode_ci",
        "--skip-character-set-client-handshake"
      ]
    healthcheck:
      test: ["CMD", 'mysqladmin', 'ping', '-h', 'localhost', '-u', 'root', '-p$$MYSQL_ROOT_PASSWORD']
      interval: 10s
      timeout: 5s
      retries: 5
      start_period: 10s

volumes:
  ruoyi-data:

networks:
  ruoyi-net:

6.参考文档

八、dockerfile 制作镜像

1.dockerfile

dockerfile 通常包含以下几个常用命令:

FROM ubuntu:18.04
WORKDIR /app
COPY . .
RUN make .
CMD python app.py
EXPOSE 80
  • FROM:打包使用的基础镜像
  • WORKDIR:相当于 cd 命令,进入工作目录
  • COPY:将宿主机的文件复制到容器内
  • RUN:打包时执行的命令,相当于打包过程中在容器中执行 shell 脚本,通常用来安装应用程序所需要的依赖、设置权限、初始化配置文件等
  • CMD:运行镜像时执行的命令
  • EXPOSE:指定容器在运行时监听的网络端口,它并不会公开端口,仅起到声明的作用,公开端口需要容器运行时使用 -p 参数指定。

2.制作自己的镜像

参考我们之前的配置,制作 dockerfile 文件

  ruoyi-java:
    image: openjdk:8u342-jre
    ports:
      - 8080:8080
    volumes:
      - /home/app/ruoyi-admin.jar:/usr/local/src/ruoyi-admin.jar
    command: [ "java", "-jar", "/usr/local/src/ruoyi-admin.jar" ]
    networks:
      - ruoyi-net
    depends_on:
      - ruoyi-db

编写 dockerfile 文件

FROM openjdk:8u342-jre
WORKDIR /app
COPY ./ruoyi-admin.jar .
CMD [ "java", "-jar", "ruoyi-admin.jar" ]
EXPOSE 8080

docker build . 打包

Docker 构建镜像
Docker 构建镜像

docker images 查看镜像 id

查看镜像
查看镜像

docker tag 79d007b05ff6 ruoyi-app:4.7.4-jar 设置镜像的标签

3.image 镜像与 layer 层

image 文件由一系列层构建而成,dockerfile 每一个命令都会生成一个层。每一层都是只读的。

例如前面我们制作镜像,就产生了 4 个层。

镜像构建
镜像构建

也可以使用 docker image history ruoyi-java:4.7.4 命令查看

查看镜像构建历史
查看镜像构建历史

创建容器时,会创建一个新的可写层,通常称为 “容器层”。对正在运行的容器所做的所有更改(如写入新文件、修改现有文件和删除文件)都将写入容器层,而不会修改镜像。

镜像分层
镜像分层

4.多阶段构建

在构建基于 Java 的应用程序时,需要一个 JDK 将源代码编译为 Java 字节码。但是,在生产中不需要该 JDK。多阶段构建可以将生成时依赖与运行时依赖分开,减小整个 image 文件大小。

Maven/Tomcat 示例

使用 Maven 来构建应用,在最终的 image 中不需要包含 maven。我们可以使用多阶段构建,每一个阶段从 FROM 开始,最终的 image 只会从最后一个阶段构建,不会包含前面阶段产生的层,因此可以减少镜像体积。

FROM maven AS build
WORKDIR /source
COPY . .
RUN mvn package

FROM  openjdk:8u342-jre
WORKDIR /app
COPY --from=build /source/ruoyi-admin/target/ruoyi-admin.jar .
EXPOSE 80
ENTRYPOINT ["java","-jar","ruoyi-admin.jar"]

docker build -t ruoyi-jar:4.7.4 .

# project name
name: "app"

services:

  ruoyi-java:
    image: ruoyi-jar:4.7.4
    command: [
      "--server.port=8080",
      "--ruoyi.profile=/home/ruoyi/uploadPath",
      "--spring.datasource.druid.master.url=jdbc:mysql://ruoyi-db:3306/ry?useUnicode=true&characterEncoding=utf8",
      "--spring.datasource.druid.master.username=root",
      "--spring.datasource.druid.master.password=123456"
    ]
    ports:
      - 8080:8080
    networks:
      - ruoyi-net
    healthcheck:
      test: [ "CMD", "curl", "-f", "http://localhost:8080" ]
      interval: 10s
      timeout: 5s
      retries: 5
      start_period: 10s
    depends_on:
      ruoyi-db:
        condition: service_healthy

  ruoyi-db:
    image: mysql:5.7
    environment:
      - MYSQL_ROOT_PASSWORD=123456
      - MYSQL_DATABASE=ry
    command: [
      "--character-set-server=utf8mb4",
      "--collation-server=utf8mb4_general_ci",
      "--skip-character-set-client-handshake"
    ]
    volumes:
      - /home/app/sql:/docker-entrypoint-initdb.d
      - ruoyi-data:/var/lib/mysql
    networks:
      - ruoyi-net
    healthcheck:
      test: [ "CMD", 'mysqladmin', 'ping', '-h', 'localhost', '-u', 'root', '-p$$MYSQL_ROOT_PASSWORD' ]
      interval: 10s
      timeout: 5s
      retries: 5
      start_period: 10s

volumes:
  ruoyi-data:

networks:
  ruoyi-net:

ENTRYPOINTCMD 的区别

dockerfile 应该至少包含一个 ENTRYPOINTCMD

ENTRYPOINT 指定容器启动时执行的默认程序,一般运行容器时不会被替换或覆盖。

除非使用 --entrypoint 进行指定。

docker run -it --entrypoint /bin/bash redis 

CMD 可以在启动容器时被替换和覆盖。

例如 docker run -it --rm mysql:5.7 /bin/bash

如果镜像中 ENTRYPOINTCMD 都存在,则 CMD 将作为 ENTRYPOINT 的参数使用。

5.参考文档

九、私有仓库

1.docker registry

我们可以使用 docker push 将自己的 image 推送到 docker hub 中进行共享,但是在实际工作中,很多公司的代码不能上传到公开的仓库中,因此我们可以创建自己的镜像仓库。

docker 官网提供了一个 docker registry 的私有仓库项目,可以方便的通过 docker 部署。

docker run -d -p 5000:5000 --restart always --name registry registry:2

docker image tag ruoyi-java:4.7.4 localhost:5000/ruoyi-java:4.7.4

docker push localhost:5000/ruoyi-java:4.7.4

docker pull localhost:5000/ruoyi-java:4.7.4

如果遇到以下错误:

构建异常
构建异常

这是因为 docker push 默认使用 HTTPS 协议,而服务端的 registry 仓库使用的是 HTTP

解决这个问题,需要修改 /etc/docker/daemon.json,加入

"insecure-registries": ["192.168.56.108:5000"]

2.harbor

habor 是一个功能更强大镜像仓库,它具有完整的权限控制和 Web 界面,更符合我们的实际工作场景。

下载 bitname 发布的 harbor 镜像配置包:https://github.com/bitnami/containers/archive/main.tar.gzopen in new window

mkdir harbor
tar xzvf containers-main.tar.gz
cd containers-main/bitnami/harbor-portal
docker compose up -d

浏览器访问:http://192.168.56.108open in new window,默认用户名/密码:admin/bitnami

Harbor
Harbor

3.保存与加载镜像

当我们处于离线状态,比如在很多内网上不能访问互联网,这时候不能通过镜像仓库的方式共享镜像,我们可以使用导出和导入功能,手动拷贝镜像。

  • docker save:会包含所有层,以及所有标签 + 版本信息。
  • docker save alpine:3.15 > alpine-3.15.tar:保存镜像
  • docker rmi alpine:3.15:删除本地镜像
  • docker load < alpine-3.15.tar:加载镜像

说明

  • 不要跟 exportimport 命令混淆
  • docker save/load IMAGEsaveload 操作的是镜像
  • docker export/import CONTAINERexportimport 操作对象是容器
  • 镜像包含多个层,每一层都不可变,save 保存的信息包含每个层和所有标签 + 版本信息。
  • 容器运行的时候会创建一个可写入的容器层,所有的更改都写入容器层,export 导出的只有容器层,不包含父层和标签信息。
镜像分层
镜像分层

十、windows 安装 docker

  • Docker Engine 我们通常所说的 docker,是指 Docker Engine,它是一种容器化技术,用于创建和运行容器。

  • Docker Desktop Docker Desktop 是一个用于操作 docker的 GUI 图形界面化工具,它包含 Docker Engine。

1.Windows 下安装 Docker Desktop

注意

  1. 如果是 Windows 7、Windows8 用户,推荐在虚拟机中安装 docker。

系统版本要求

在 PowerShell 中使用 winver 命令查看系统版本

  • Windows 11
  • Windows 10 21H1 或更高版本

必要条件

  1. 在 “任务管理器” 的 “性能” 选项里查看虚拟化是否启用。如果没有,需要在 BIOS 中开启。
任务管理器
任务管理器
  1. 安装“虚拟机平台”和 WSL
控制面板
控制面板

或者以管理员身份运行以下命令:

Enable-WindowsOptionalFeature -Online -FeatureName VirtualMachinePlatform -All -NoRestart
Enable-WindowsOptionalFeature -Online -FeatureName Microsoft-Windows-Subsystem-Linux -All -NoRestart
  1. 设置 Hypervisor 开机自动启动

一些程序(例如旧版本的 Virtual Box 或某些游戏)会禁用 Hypervisor 自动启动,可能会使 docker 无法正常运行。

bcdedit /set hypervisorlaunchtype auto
  1. 安装 WSL2 内核更新包
  1. 将 WSL 2 设置为默认版本
wsl --set-default-version 2

重启系统。

安装 Docker Desktop

下载 Docker Desktop for Windowsopen in new window 进行安装。出现以下界面,安装成功。

Docker Desktop for Window
Docker Desktop for Window

配置镜像站

由于访问 docker hub 网络比较慢,因此需要配置国内的容器仓库镜像站。

配置镜像站
配置镜像站
{
  "builder": {
    "gc": {
      "defaultKeepStorage": "20GB",
      "enabled": true
    }
  },
  "experimental": false,
  "features": {
    "buildkit": true
  },
  "registry-mirrors": [
    "https://reg-mirror.qiniu.com/",
    "https://docker.mirrors.ustc.edu.cn/",
    "https://hub-mirror.c.163.com/"
  ]
}

2.注意事项

  • 挂载路径

注意:windows 下路径使用 \ 作为分割符

docker run --rm -it -v D:\docker\data1:/work1 alpine

或者采用 Linux 路径写法

docker run --rm -it -v /D/docker/data2:/work2 alpine
  • dockerfile 换行符

Docker 容器基于 Linux 运行环境,容器内文件必须使用 Linux 样式的换行符 \n,不能使用 windows 换行符 \r\n

例如 dockerfile 文件,windows 下的一些工具会默认使用 windows 换行符,可能导致这些文件在容器中显示语法错误。

3.参考文档

十一、安装 portainer

1.portainer

Portainer 是一个可视化的 Docker 管理系统,功能十分全面,提供状态显示面板、应用模板快速部署、容器镜像、网络、数据卷的基本操作、事件日志显示、容器控制台操作、登录用户管理和控制等功能。

Portainer
Portainer

2.使用 docker 部署 portainer

## 创建挂载卷
docker volume create portainer_data

## 启动容器
docker run -d -p 8000:8000 -p 9443:9443 \
-v /var/run/docker.sock:/var/run/docker.sock \
-v portainer_data:/data \
--restart=always \
--name portainer portainer/portainer-ce:2.14.2

打开浏览器访问:https://宿主机IP:9443/open in new window

3.重启策略

容器的重启策略。

docker run --restart=always IMAGE:TAG

说明

  • --restart=no 默认为 no,不会自动重启容器。
  • --restart=always 当容器停止运行时,总是重启容器。当我们重启 docker 进程时,也会自动重启容器。如果我们使用 docker stop 停止容器时,则不会自动重启容器。
  • --restart=on-failure:10 仅当容器以异常状态退出时,才重启容器。若重启不成功,最多尝试重启 10 次。
  • --restart=unless-stopped 当我们重启 docker 时,若这个容器已经处停止状态,则不进行自动重启。

十二、参考