Docker 入门教程
一、docker 简介
1.什么是 docker
Docker 是一个开源的应用容器引擎,由于 docker 影响巨大,今天也用 "Docker" 指代容器化技术。
2.docker 的优势
一键部署,开箱即用
容器使用基于 image 镜像的部署模式,image 中包含了运行应用程序所需的一切:代码、运行时、系统工具、系统库和配置文件。
无论是单个程序还是多个程序组成的复杂服务,或者分布式系统,都可以使用 docker run
或 docker compose up
命令一键部署,省去了大量搭建、配置环境、调试和排查错误的时间。
一次打包,到处运行
Docker 为容器创建了行业标准,使容器成为了软件交付过程中的一种标准化格式,将软打包成容器镜 (image),能够使软件在不同环境下运行一致,应用程序可以快速可靠地从一个环境移植到另外一个环境,并确保在所有的部署目标(例如开发、测试、生产环境)上都按预期运行,从而避免了 “在我电脑上是好的,怎么到你那却不能用了?” 的问题。
3.容器与虚拟机
容器包括应用程序及其所有依赖项。容器运行时,与宿主机共享操作系统内核,容器在 Linux 内核层面(使用 Cgroups 和 namespaces)来实现进程间的隔离,容器在主机操作系统上的用户空间中作为独立进程运行。
因此,容器相比于虚拟机更加轻量化,它体积小,启动快,占用资源少,性能好。
虚拟机使用虚拟化技术,应用程序运行在完整的操作系统(OS)之上,因此占用的资源更多,安装更复杂。
但是由于容器与宿主机共享内核,所以在隔离性和安全性方面不如虚拟机。
4.参考文档
- 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,请先删除。
# 移除自带的 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 架构
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
-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
- 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.参考文档
- 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/
五、docker 存储
将数据存储在容器中,一旦容器被删除,数据也会被删除。同时也会使容器变得越来越大,不方便恢复和迁移。
将数据存储到容器之外,这样删除容器也不会丢失数据。一旦容器故障,我们可以重新创建一个容器,将数据挂载到容器里,就可以快速的恢复。
1.存储方式
docker 提供了以下存储选项
- volume 卷
卷存储在主机文件系统分配一块专有存储区域,由 Docker(在 Linux 上)管理,并且与主机的核心功能隔离。非 Docker 进程不能修改文件系统的这一部分。卷是在 Docker 中持久保存数据的最佳方式。
- bind mount 绑定挂载
绑定挂载可以将主机文件系统上目录或文件装载到容器中,但是主机上的非 Docker 进程可以修改它们,同时在容器中也可以更改主机文件系统,包括创建、修改或删除文件或目录,使用不当,可能会带来安全隐患。
- tmpfs 临时挂载
tmpfs 挂载仅存储在主机系统的内存中,从不写入主机系统的文件系统。当容器停止时,数据将被删除。
绑定挂载(bind mount)
绑定挂载适用以下场景:
- 将配置文件从主机共享到容器。
- 在 Docker 主机上的开发环境和容器之间共享源代码或编译目录。
- 例如,可以将 Maven 的
target/
目录挂载到容器中,每次在主机上用 Maven 打包项目时,容器内都可以使用新编译的程序包。
- 例如,可以将 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.参考文档
- 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
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/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
可以通过以下两种解决方法
修改运行参数
使用环境变量 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
wordpress 是一个著名的开源博客系统。
将以下内容保存到本地的 docker-compose.yml
文件中。
docker compose
命令启动时,默认在当前目录下寻找 compose.yaml
或 compose.yml
,为了兼容之前的版本,也会查找 docker-compose.yaml
或 docker-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。
depends_on
只能保证容器的启动和销毁顺序,不能确保依赖的容器是否 ready。
version: "3.9"
services:
web:
build: .
depends_on:
- db
- redis
redis:
image: redis
db:
image: postgres
在这个例子中,depends_on
只能保证 web
容器在 db
,redis
之后启动,不会关注他们的状态是否启动完成或准备就绪。
要确保应用服务在数据库初始化完成后再启动,需要配合 condition 和 healthcheck 使用。
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.参考文档
- 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 制作镜像
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 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 /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:
ENTRYPOINT
和 CMD
的区别
dockerfile
应该至少包含一个 ENTRYPOINT
或 CMD
。
ENTRYPOINT
指定容器启动时执行的默认程序,一般运行容器时不会被替换或覆盖。
除非使用 --entrypoint
进行指定。
docker run -it --entrypoint /bin/bash redis
CMD
可以在启动容器时被替换和覆盖。
例如 docker run -it --rm mysql:5.7 /bin/bash
如果镜像中 ENTRYPOINT
和 CMD
都存在,则 CMD
将作为 ENTRYPOINT
的参数使用。
5.参考文档
- 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
九、私有仓库
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.gz
mkdir harbor
tar xzvf containers-main.tar.gz
cd containers-main/bitnami/harbor-portal
docker compose up -d
浏览器访问:http://192.168.56.108,默认用户名/密码:admin/bitnami
3.保存与加载镜像
当我们处于离线状态,比如在很多内网上不能访问互联网,这时候不能通过镜像仓库的方式共享镜像,我们可以使用导出和导入功能,手动拷贝镜像。
docker save
:会包含所有层,以及所有标签 + 版本信息。docker save alpine:3.15 > alpine-3.15.tar
:保存镜像docker rmi alpine:3.15
:删除本地镜像docker load < alpine-3.15.tar
:加载镜像
说明
- 不要跟
export
和import
命令混淆docker save/load IMAGE
:save
和load
操作的是镜像docker export/import CONTAINER
:export
和import
操作对象是容器- 镜像包含多个层,每一层都不可变,save 保存的信息包含每个层和所有标签 + 版本信息。
- 容器运行的时候会创建一个可写入的容器层,所有的更改都写入容器层,export 导出的只有容器层,不包含父层和标签信息。
十、windows 安装 docker
Docker Engine 我们通常所说的 docker,是指 Docker Engine,它是一种容器化技术,用于创建和运行容器。
Docker Desktop Docker Desktop 是一个用于操作 docker的 GUI 图形界面化工具,它包含 Docker Engine。
1.Windows 下安装 Docker Desktop
注意
- 如果是 Windows 7、Windows8 用户,推荐在虚拟机中安装 docker。
系统版本要求
在 PowerShell 中使用 winver
命令查看系统版本
- Windows 11
- Windows 10 21H1 或更高版本
必要条件
- 在 “任务管理器” 的 “性能” 选项里查看虚拟化是否启用。如果没有,需要在 BIOS 中开启。
- 安装“虚拟机平台”和 WSL
或者以管理员身份运行以下命令:
Enable-WindowsOptionalFeature -Online -FeatureName VirtualMachinePlatform -All -NoRestart
Enable-WindowsOptionalFeature -Online -FeatureName Microsoft-Windows-Subsystem-Linux -All -NoRestart
- 设置 Hypervisor 开机自动启动
一些程序(例如旧版本的 Virtual Box 或某些游戏)会禁用 Hypervisor 自动启动,可能会使 docker 无法正常运行。
bcdedit /set hypervisorlaunchtype auto
- 安装 WSL2 内核更新包
- 将 WSL 2 设置为默认版本
wsl --set-default-version 2
重启系统。
安装 Docker Desktop
下载 Docker Desktop for Windows 进行安装。出现以下界面,安装成功。
配置镜像站
由于访问 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.参考文档
- https://docs.docker.com/desktop/windows/wsl/
- https://docs.microsoft.com/zh-cn/windows/wsl/install-manual
- https://docs.docker.com/desktop/troubleshoot/topics/
- https://forums.virtualbox.org/viewtopic.php?f=6&t=105951
- https://docs.docker.com/desktop/faqs/windowsfaqs/
十一、安装 portainer
1.portainer
Portainer 是一个可视化的 Docker 管理系统,功能十分全面,提供状态显示面板、应用模板快速部署、容器镜像、网络、数据卷的基本操作、事件日志显示、容器控制台操作、登录用户管理和控制等功能。
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/
3.重启策略
容器的重启策略。
docker run --restart=always IMAGE:TAG
说明
--restart=no
默认为 no,不会自动重启容器。--restart=always
当容器停止运行时,总是重启容器。当我们重启 docker 进程时,也会自动重启容器。如果我们使用docker stop
停止容器时,则不会自动重启容器。--restart=on-failure:10
仅当容器以异常状态退出时,才重启容器。若重启不成功,最多尝试重启 10 次。--restart=unless-stopped
当我们重启 docker 时,若这个容器已经处停止状态,则不进行自动重启。