# Docker

原文地址:https://www.yuque.com/wukong-zorrm/xwas40 (opens new window)

尚硅谷Docker笔记

# 一、docker简介

# 1.什么是docker

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

# 2.docker的优势

# 一键部署,开箱即用

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

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

# 一次打包,到处运行

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

# 3.容器与虚拟机

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

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

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

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

参考文档:

https://www.docker.com/resources/what-container/#/package_software

https://docs.docker.com/engine/security/

# 二、安装docker

# 1.Windows下安装Docker

注意事项:

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

# 2.linux下安装Docker

安装环境:CentOS 7.3+

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

sudo yum remove docker \
                  docker-client \
                  docker-client-latest \
                  docker-common \
                  docker-latest \
                  docker-latest-logrotate \
                  docker-logrotate \
                  docker-engine

安装仓库

sudo yum install -y yum-utils

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,运行hello world查看是否成功

sudo systemctl start docker
sudo docker run hello-world

配置国内镜像仓库地址:

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

{
  "registry-mirrors": [
    "https://registry.docker-cn.com",
    "http://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架构

img

# 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://192.168.56.106:8080

img

-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 \
	--format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' db-mysql

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

docker start db-mysql 启动容器

docker stop 关闭容器

docker restart 重启容器

docker rm 删除容器

# 前台交互运行

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

docker run -it --rm mysql:5.7 mysql -h172.17.0.2 -uroot -p

-it 使用交互模式,可以在控制台里输入、输出

--rm**在容器退出时自动删除容器。**一般在使用客户端程序时使用此参数。

如果每次使用客户端都创建一个新的容器,这样将占用大量的系统空间。

mysql -h172.17.0.2 -uroot -p表示启动容器时执行的命令。

  • docker exec在运行的容器中执行命令,一般配合-it参数使用交互模式
docker exec -it db-mysql /bin/bash

# 3.常用命令

  • docker ps 查看正在运行的容器
  • docker ps -a 查看所有容器,包括正在运行和停止的
  • docker inspect 查看容器的信息
  • docker logs查看日志
  • docker cp 在容器和宿主机间复制文件
docker cp ./some_file 容器名:/work
docker cp 容器名:/var/logs/ /tmp/app_logs

参考文档:

https://docs.docker.com/get-started/overview/

https://docs.docker.com/engine/reference/run/

# 四、docker 网络

# 默认网络

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

img

  • bridge桥接网络

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

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

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

  • host

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

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

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

  • none

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

# 用户自定义网络

创建用户自定义网络

docker network create my-net

将已有容器连接到此网络

docker network connect my-net db-mysql

创建容器时指定网络。

docker run -it --rm --network my-net mysql:5.7 mysql -h**db-mysql** -uroot -p

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

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

# 主机名解析

# hostname

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

docker run -it -d --hostname my-alpine --name my-alpine  alpine:3.15
docker inspect \
	--format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' my-alpine

img

# /etc/hosts

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

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

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

参考文档:

https://docs.docker.com/network/

https://docs.docker.com/network/bridge/

https://docs.docker.com/config/containers/container-networking/

https://docs.docker.com/network/network-tutorial-standalone/ (opens new window)

# 五、docker 存储

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

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

# 存储方式

docker 提供了以下存储选项

img

  • 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

](https://docs.docker.com/engine/reference/commandline/volume_create/)

参考文档:

https://docs.docker.com/storage/

https://docs.docker.com/storage/bind-mounts/

https://docs.docker.com/storage/tmpfs/

https://docs.docker.com/storage/volumes/

https://docs.docker.com/engine/reference/commandline/volume_create/

# 六、部署自己的应用

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

ruoyi官方文档:http://doc.ruoyi.vip/ruoyi/

源码下载:https://gitee.com/y_project/RuoYi/tree/v4.7.4/

将源码编译打包成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

# 部署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

# 部署应用

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

# 解决乱码问题

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

参考:https://github.com/docker-library/mysql/issues/131

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

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

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

# 1.修改运行参数

使用环境变量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 

# 2.修改配置文件

修改/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 

参考文档:

https://mariadb.com/kb/en/setting-character-sets-and-collations/

# 七、docker compose容器编排

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

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

# 一键部署wordpress

wordpress (opens 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启动/停止服务

docker compose down停止并删除容器,不会删除存储卷volume

# compose文件结构

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

version 已弃用,早期版本需要此元素。

services必要元素,定义一个或多个容器的运行参数

services中可以通过以下元素定义容器的运行参数

image 容器 镜像

ports端口映射

environment环境变量

networks容器使用的网络

volumes容器挂载的存储卷

command容器启动时执行的命令

depends_on定义启动顺序

复数形式(例如ports,networks,volumes,depends_on)参数需要传入列表

networks创建自定义网络

volumes 创建存储卷

# yaml文件语法

  • 缩进代表上下级关系
  • 缩进时不允许使用Tab键,只允许使用空格
  • : 键值对,后面必须有空格
  • -列表,后面必须有空格
  • [ ]数组
  • #注释
  • {key:value,k1:v1}map
  • | 多行文本块

如果一个文件中包含多个文档

  • ---表示一个文档的开始

还有一种常见的用法:

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

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:

# 编排自己的项目

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

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

# 容器启动顺序depends_on

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

img

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

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

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

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

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:

参考文档:

https://docs.docker.com/compose/

https://docs.docker.com/compose/compose-file/

https://docs.docker.com/compose/compose-file/#depends_on

https://docs.docker.com/compose/startup-order/

# 八、dockerfile制作镜像

# 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参数指定。

# 制作自己的镜像

参考我们之前的配置,制作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 . 打包

img

docker images 查看镜像id

img

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

img

# image镜像与layer层

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

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

img

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

img

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

img

# 多阶段构建

在构建基于 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`应该至少包含一个`ENTRYPOINT`或`CMD

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

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

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

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

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

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

参考文档:

https://docs.docker.com/get-started/09_image_best/

https://docs.docker.com/language/java/build-images/

https://docs.docker.com/storage/storagedriver/

https://docs.docker.com/develop/develop-images/dockerfile_best-practices/

https://docs.docker.com/engine/reference/run/#cmd-default-command-or-options

# 九、私有仓库

# 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

如果遇到以下错误:

img

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

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

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

# harbor

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

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

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

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

img

# 保存与加载image

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

docker save会包含所有层,以及所有标签 + 版本信息。

docker save alpine:3.15 > alpine-3.15.tar 保存image

docker rmi alpine:3.15 删除本地image

docker load < alpine-3.15.tar 加载image

注意:

不要跟export和import命令混淆

docker save/load IMAGE save和load操作的是镜像

docker export/import CONTAINERexport和import操作对象是容器

image包含多个层,每一层都不可变,save保存的信息包含每个层和所有标签 + 版本信息。

容器运行的时候会创建一个可写入的容器层,所有的更改都写入容器层,export导出的只有容器层,不包含父层和标签信息。

img

# 十、windows安装docker

  • Docker Engine

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

  • Docker Desktop

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

# 1.Windows下安装Docker Desktop

注意事项:

  1. 如果是Windows 7、Windows8 用户,推荐在虚拟机中安装docker。
# 1.系统版本要求

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

  • Windows 11
  • Windows 10 21H1 或更高版本
# 2.必要条件
  1. 在“任务管理器”的“性能”选项里查看虚拟化是否启用。如果没有,需要在BIOS中开启。

img

  1. 安装“虚拟机平台”和 WSL

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

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内核更新包 ●适用于 x64 计算机的 WSL2 Linux 内核更新包 (opens new window)
  2. 将 WSL 2 设置为默认版本
wsl --set-default-version 2

重启系统。

# 3.安装Docker Desktop

下载[Docker Desktop for Windows](https://desktop.docker.com/win/main/amd64/Docker Desktop Installer.exe)进行安装。出现以下界面,安装成功。

img

# 4.配置镜像站

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

img

{
  "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换行符,可能导致这些文件在容器中显示语法错误。

参考文档:

https://docs.docker.com/desktop/windows/wsl/

https://docs.microsoft.com/zh-cn/windows/wsl/install-manualhttps://docs.docker.com/desktop/troubleshoot/topics/

https://forums.virtualbox.org/viewtopic.php?f=6&t=105951

https://docs.docker.com/desktop/faqs/windowsfaqs/

# 十一、安装portainer

# portainer

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

img

# 使用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://192.168.56.105:9443/ (opens new window)

# 重启策略

容器的重启策略。

docker run --restart=always IMAGE:TAG

--restart=no 默认为no,不会自动重启容器。

--restart=always 当容器停止运行时,总是重启容器。当我们重启docker进程时,也会自动重启容器。如果我们使用docker stop停止容器时,则不会自动重启容器。

--restart=on-failure:10 仅当容器以异常状态退出时,才重启容器。若重启不成功,最多尝试重启 10次。

--restart=unless-stopped 当我们重启docker时,若这个容器已经处停止状态,则不进行自动重启。