Docker 使用教程
Docker 的简介和安装
Docker 是一个应用打包、分发、部署的工具。你也可以把它理解为一个轻量的虚拟机,它只虚拟你软件需要的运行环境,多余的一点都不要,而普通虚拟机则是一个完整而庞大的系统,包含各种不管你要不要的软件。
Docker 为什么比虚拟机快?
- Docker 有着比虚拟机更少的抽象层
- Docker 利用的是宿主机的内核,而 vm 需要 Guest OS,所以新建一个容器的时候,docker 不需要像虚拟机一样重新加载一个操作系统内核,避免引导。
特性 | 普通虚拟机 | Docker |
---|---|---|
跨平台 | 通常只能在桌面级系统运行,例如 Windows/Mac | 支持的系统非常多,各类 windows 和 Linux 都支持 |
性能 | 性能损耗大,内存占用高,因为是把整个完整系统都虚拟出来了 | 性能好,只虚拟软件所需运行环境,最大化减少没用的配置 |
自动化 | 需要手动安装所有东西 | 一个命令就可以自动部署好所需环境 |
稳定性 | 稳定性不高,不同系统差异大 | 稳定性好,不同系统都一样部署方式 |
- 打包:就是把软件所需的依赖、第三方库、软件打包到一起,变成一个安装包
- 分发:你可以把打包好的安装包上传到一个镜像仓库,其他人可以非常方便的获取和安装
- 部署:拿着安装包就可以一个命令运行起来你的应用,自动模拟出一摸一样的运行环境,不管是在
Windows/Mac/Linux
。
Docker 部署的优势?
-
常规应用开发部署方式:自己在 windows 上开发、测试 \(\to\) 到 Linux 服务器配置运行环境部署
-
Docker 开发部署流程:自己在 windows 上开发 \(\to\) 打包为 Docker 镜像(可以理解为软件安装包)\(\to\) 各种服务器上只需要一个命令部署好,确保了不同机器上跑都是一致的运行环境,例如易文档、
SVNBucket
的私有化部署就是用 Docker,轻松应对客户的各种服务器。
Docker 通常用来做什么?
- 应用分发、部署,方便传播给他人安装,特别是开源软件和提供私有部署的应用
- 快速安装测试 / 学习软件,用完就丢(类似小程序),不把时间浪费在安装软件上
- 多个版本共存,不污染系统
- Windows 上体验各种 Linux 系统
理解两个重要概念:
- 镜像 (image):可以理解为软件安装包,用来打包软件运行环境和基于运行环境开发的软件,它包含运行某个软件所需的所有内容,包括代码、库、环境变量和配置文件。一个模板,可以通过这个模板来创建容器服务,通过这个镜像可以创建多个容器
- 容器 (container)):软件安装后的状态,每个软件运行环境都是独立的、隔离的,称之为容器,最终服务运行或者项目运行就是在容器中
- 仓库 (repository):存放镜像的地方,分为公有仓库和私有仓库
安装链接:
-
桌面版:https://www.docker.com/products/docker-desktop
- 直接按步骤操作
-
服务器版:https://docs.docker.com/engine/install/#server
- 使用
Xshell
连接远程服务器 - 查看 Linux 发行版名称和版本号:
uname -r
# 系统版本 [root@fantastic-lasers-1 ~]# uname -r 4.10.4-1.el7.elrepo.x86_64 [root@fantastic-lasers-1 ~]# cat /etc/os-release NAME="CentOS Linux" VERSION="7 (Core)" ID="centos" ID_LIKE="rhel fedora" VERSION_ID="7" PRETTY_NAME="CentOS Linux 7 (Core)" ANSI_COLOR="0;31" CPE_NAME="cpe:/o:centos:centos:7" HOME_URL="https://www.centos.org/" BUG_REPORT_URL="https://bugs.centos.org/" CENTOS_MANTISBT_PROJECT="CentOS-7" CENTOS_MANTISBT_PROJECT_VERSION="7" REDHAT_SUPPORT_PRODUCT="centos" REDHAT_SUPPORT_PRODUCT_VERSION="7"
- 查询安装文档,卸载旧版本,然后通过 repository 安装
# 1. 卸载旧版本 sudo yum remove docker \ docker-client \ docker-client-latest \ docker-common \ docker-latest \ docker-latest-logrotate \ docker-logrotate \ docker-engine # 2. 需要的安装包 sudo yum install -y yum-utils # 默认是国外的地址 sudo yum-config-manager \ --add-repo \ https://download.docker.com/linux/centos/docker-ce.repo # 换成阿里云的地址 sudo yum-config-manager \ --add-repo \ https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo # 更新 yum 软件包索引 yum makecache fast # 安装 docker sudo yum install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin # start docker sudo systemctl start docker # 验证是否启动成功 sudo docker version
[root@fantastic-lasers-1 ~]# docker version Client: Docker Engine - Community Version: 23.0.4 API version: 1.42 Go version: go1.19.8 Git commit: f480fb1 Built: Fri Apr 14 10:36:38 2023 OS/Arch: linux/amd64 Context: default Server: Docker Engine - Community Engine: Version: 23.0.4 API version: 1.42 (minimum version 1.12) Go version: go1.19.8 Git commit: cbce331 Built: Fri Apr 14 10:34:14 2023 OS/Arch: linux/amd64 Experimental: false containerd: Version: 1.6.20 GitCommit: 2806fc1057397dbaeefbea0e4e17bddfbd388f38 runc: Version: 1.1.5 GitCommit: v1.1.5-0-gf19387a docker-init: Version: 0.19.0 GitCommit: de40ad0
- 运行 hello-world
sudo docker run hello-world 输出: Unable to find image 'hello-world:latest' locally latest: Pulling from library/hello-world 2db29710123e: Pull complete Digest: sha256:4e83453afed1b4fa1a3500525091dbfca6ce1e66903fd4c01ff015dbcb1ba33e Status: Downloaded newer image for hello-world:latest Hello from Docker! This message shows that your installation appears to be working correctly. To generate this message, Docker took the following steps: 1. The Docker client contacted the Docker daemon. 2. The Docker daemon pulled the "hello-world" image from the Docker Hub. (amd64) 3. The Docker daemon created a new container from that image which runs the executable that produces the output you are currently reading. 4. The Docker daemon streamed that output to the Docker client, which sent it to your terminal. To try something more ambitious, you can run an Ubuntu container with: $ docker run -it ubuntu bash
- 查看一下下载的 hello-world 镜像
sudo docker images 输出: REPOSITORY TAG IMAGE ID CREATED SIZE hello-world latest feb5d9fea6a5 19 months ago 13.3kB
- 卸载 Docker Engine:如果需要卸载再卸载
# Uninstall the Docker Engine, CLI, Containerd, and Docker Compose packages: 卸载依赖 sudo yum remove docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin docker-ce-rootless-extras # Images, containers, volumes, or customized configuration files on your host are not automatically removed. To delete all images, containers, and volumes: 删除目录 sudo rm -rf /var/lib/docker sudo rm -rf /var/lib/containerd
- 使用
-
- 登录阿里云网站 \(\to\) 产品 \(\to\) 容器镜像服务
- 获取镜像加速地址:https://6jotehk6.mirror.aliyuncs.com
- 配置镜像加速器
sudo mkdir -p /etc/docker sudo tee /etc/docker/daemon.json <<-'EOF' { "registry-mirrors": ["https://6jotehk6.mirror.aliyuncs.com"] } EOF sudo systemctl daemon-reload sudo systemctl restart docker
Docker 的底层原理:
Docker
是一个Client-Server
结构的系统,Docker 的守护进程运行在主机上,通过 Socket 从客户端访问。DockerServer
接收到Docker-Client
的指令,就会执行这个命令
Docker 的常用命令
帮助命令
docker version # 显示 docker 的版本信息
docker info # 显示 docker 的系统信息,包括镜像和容器的数量
docker 命令 --help
镜像命令
docker images
:查看本地主机上的所有镜像
[root@fantastic-lasers-1 ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
hello-world latest feb5d9fea6a5 19 months ago 13.3kB
Options:
-a, --all 显示全部镜像
-q, --quiet 只显示镜像的 ID
docker search
:搜索镜像
[root@fantastic-lasers-1 ~]# docker search mysql
NAME DESCRIPTION STARS OFFICIAL AUTOMATED
mysql MySQL is a widely used, open-source relation… 14049 [OK]
mariadb MariaDB Server is a high performing open sou… 5367 [OK]
percona Percona Server is a fork of the MySQL relati… 605 [OK]
phpmyadmin phpMyAdmin - A web interface for MySQL and M… 784 [OK]
# 可选项,通过搜索来过滤
--filter=STARS=3000 # 搜索出来的镜像就是 STARS 大于 3000 的
docker pull [:tag]
:下载镜像
[root@fantastic-lasers-1 ~]# docker pull mysql
Using default tag: latest # 如果不写 tag,默认就是 latest
latest: Pulling from library/mysql
72a69066d2fe: Pull complete # 分层下载,docker image 的核心,联合文件系统
93619dbc5b36: Pull complete
99da31dd6142: Pull complete
626033c43d70: Pull complete
37d5d7efb64e: Pull complete
ac563158d721: Pull complete
d2ba16033dad: Pull complete
688ba7d5c01a: Pull complete
00e060b6d11d: Pull complete
1c04857f594f: Pull complete
4d7cfa90e6ea: Pull complete
e0431212d27d: Pull complete
Digest: sha256:e9027fe4d91c0153429607251656806cc784e914937271037f7738bd5b8e7709 # 签名
Status: Downloaded newer image for mysql:latest
docker.io/library/mysql:latest # 真实地址
# 等价于
docker pull docker.io/library/mysql:latest
### 指定版本下载 ###
docker pull mysql:5.7
# https://hub.docker.com/_/mysql 找到相关版本
[root@fantastic-lasers-1 ~]# docker pull mysql:5.7
5.7: Pulling from library/mysql
72a69066d2fe: Already exists
93619dbc5b36: Already exists
99da31dd6142: Already exists
626033c43d70: Already exists
37d5d7efb64e: Already exists
ac563158d721: Already exists
d2ba16033dad: Already exists
0ceb82207cd7: Pull complete
37f2405cae96: Pull complete
e2482e017e53: Pull complete
70deed891d42: Pull complete
Digest: sha256:f2ad209efe9c67104167fc609cca6973c8422939491c9345270175a300419f94
Status: Downloaded newer image for mysql:5.7
docker.io/library/mysql:5.7
docker rmi -f 容器id 容器id 容器id
:删除镜像,一般通过 id 来删除docker rmi -f $(docker images -aq)
:批量删除
[root@fantastic-lasers-1 ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
mysql 5.7 c20987f18b13 16 months ago 448MB
mysql latest 3218b38490ce 16 months ago 516MB
hello-world latest feb5d9fea6a5 19 months ago 13.3kB
[root@fantastic-lasers-1 ~]# docker rmi -f c20987f18b13
Untagged: mysql:5.7
Untagged: mysql@sha256:f2ad209efe9c67104167fc609cca6973c8422939491c9345270175a300419f94
Deleted: sha256:c20987f18b130f9d144c9828df630417e2a9523148930dc3963e9d0dab302a76
Deleted: sha256:6567396b065ee734fb2dbb80c8923324a778426dfd01969f091f1ab2d52c7989
Deleted: sha256:0910f12649d514b471f1583a16f672ab67e3d29d9833a15dc2df50dd5536e40f
Deleted: sha256:6682af2fb40555c448b84711c7302d0f86fc716bbe9c7dc7dbd739ef9d757150
Deleted: sha256:5c062c3ac20f576d24454e74781511a5f96739f289edaadf2de934d06e910b92
docker history 镜像id
:查看镜像的构建过程
容器命令
说明:我们有了镜像才可以创建容器。
-
下载一个 centos 镜像来测试学习:
docker pull centos
-
新建容器并启动
docker run [可选参数] image
# 参数说明
--name="Name" 容器名字
-d 后台方式运行
-it 使用交互方式运行,进入容器查看内容
-p 指定容器的端口 -p 8080:8080 主机端口:容器端口
-P 随机指定端口
# 测试:启动并进入容器
[root@fantastic-lasers-1 ~]# docker run -it centos /bin/bash
# 查看容器内的 centos
[root@7f71ee15591d /]# ls
bin etc lib lost+found mnt proc run srv tmp var
dev home lib64 media opt root sbin sys usr
# 从容器中退出,回到主机
[root@7f71ee15591d /]# exit
exit
[root@fantastic-lasers-1 ~]#
docker ps
:列出所有运行中容器
[root@fantastic-lasers-1 ~]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
7f71ee15591d centos "/bin/bash" 3 minutes ago Exited (0) About a minute ago wonderful_bhabha
0948f34b9e54 feb5d9fea6a5 "/hello" 3 hours ago Exited (0) 3 hours ago sweet_lamarr
5efeea0262e2 feb5d9fea6a5 "/hello" 3 hours ago Exited (0) 3 hours ago pensive_benz
# 参数说明
-a 列出当前正在运行的容器 + 带出历史运行过的容器
-n=? 显示最近创建的容器
-q 只显示容器的编号
exit
:退出容器
exit # 直接容器停止并退出
ctrl + P + Q # 容器不停止退出
docker rm
:删除容器
docker rm 容器id # 删除指定的容器,不能删除正在运行的容器,如果要强制删除 + -f
docker rm -f $(docker ps -aq) # 删除所有容器
docker ps -a -q|xargs docker rm # 删除所有容器
- 启动和停止容器的操作
docker start 容器id # 启动容器
docker restart 容器id # 重启容器
docker stop 容器id # 停止当前正在运行的容器
docker kill 容器id # 强制停止当前容器
常用的其他命令
docker run -d 镜像名
:后台启动容器
[root@fantastic-lasers-1 ~]# docker run -d centos
1457cf8b498c295b94c06906eb4a3bfdc95951065bc5078350508cca7d35d741
# 问题:docker ps 发现 centos 停止了
# 常见的坑:docker 容器使用后台运行,就必须要有一个前台进程,docker 发现没有应用,就会自动停止
# nginx,容器启动后,发现自己没有提供服务,就会立刻停止,就是没有程序了
docker logs
:查看日志
# --tail num: 要显示的日志的条数
docker logs -tf --tail 10 1cf8c5669c1b
# -t: 显示日志条目的时间戳
# -f: 实时跟踪日志输出
# --tail num: 要显示的日志条数
# 自己写一段 shell 脚本
"while true;do echo lockegogo;sleep 1;done"
[root@fantastic-lasers-1 ~]# docker run -d centos /bin/sh -c "while true;do echo lockegogo;sleep 1;done"
[root@fantastic-lasers-1 ~]# docker ps
CONTAINER ID IMAGE
2e6172cd4191 centos
# 显示日志
[root@fantastic-lasers-1 ~]# docker logs -tf --tail 10 2e6172cd4191
2023-04-22T03:02:23.909868744Z lockegogo
2023-04-22T03:02:24.912591695Z lockegogo
2023-04-22T03:02:25.915164808Z lockegogo
2023-04-22T03:02:26.917449632Z lockegogo
2023-04-22T03:02:27.919790967Z lockegogo
top
:查看容器里的进程信息
[root@fantastic-lasers-1 ~]# docker top 2e6172cd4191
UID PID PPID C STIME TTY
root 17626 17602 0 11:00 ?
root 18393 17626 0 11:05 ?
inspect
:查看镜像的元数据
[root@fantastic-lasers-1 ~]# docker inspect 2e6172cd4191
exec
:进入当前正在运行的容器
# 容器通常都是使用后台方式运行的,我们有时需要进入容器,修改一些配置
# 命令 1
docker exec -it 容器id bashShell
# 测试
[root@fantastic-lasers-1 ~]# docker exec -it 2e6172cd4191 /bin/bash
[root@2e6172cd4191 /]# ls
bin etc lib lost+found mnt proc run srv tmp var
dev home lib64 media opt root sbin sys usr
[root@2e6172cd4191 /]# ps -ef
UID PID PPID C STIME TTY TIME CMD
root 1 0 0 03:00 ? 00:00:00 /bin/sh -c while true;do echo lockegogo;sleep 1;done
root 740 0 0 03:12 pts/0 00:00:00 /bin/bash
root 768 1 0 03:12 ? 00:00:00 /usr/bin/coreutils --coreutils-prog-shebang=sleep /usr/
root 769 740 0 03:12 pts/0 00:00:00 ps -ef
# 命令 2
docker attach 容器id
[root@fantastic-lasers-1 ~]# docker attach 2e6172cd4191
正在执行当前的代码...
# 区别
# docker exec: 进入容器后开启一个新的终端,可以在里面操作
# docker attach: 进入容器正在执行的终端,不会启动新的进程
cp
:从容器内拷贝文件到主机
# m
docker cp 容器id:容器内路径 目的地主机路径
# 进入容器内部
[root@fantastic-lasers-1 ~]# docker attach 2e6172cd4191
[root@fantastic-lasers-1 ~]# cd /home
# 在容器内新建一个文件
[root@fantastic-lasers-1 ~]# touch test.java
[root@fantastic-lasers-1 ~]# exit
# 容器是否在运行无关进行,只要容器存在,容器内的文件就会一直存在
[root@fantastic-lasers-1 ~]# docker ps -a
# 将文件拷贝出来到主机上
[root@fantastic-lasers-1 ~]# docker cp 2e6172cd4191:/home/test.java /home
# 拷贝是一个手动过程,未来我们1使用 -v 卷的技术,可以实现自动同步
Docker 快速安装软件
直接安装的缺点:
- 安装麻烦,可能有各种依赖,运行报错。例如:
WordPress
,ElasticSearch
,Redis
,ELK
- 可能对 Windows 并不友好,运行有各种兼容问题,软件只支持 Linux 上跑
- 不方便安装多版本软件,不能共存。
- 电脑安装了一堆软件,拖慢电脑速度。
- 不同系统和硬件,安装方式不一样
Docker 安装的优点:
- 一个命令就可以安装好,快速方便
- 有大量的镜像,可直接使用
- 没有系统兼容问题,Linux 专享软件也照样跑
- 支持软件多版本共存
- 用完就丢,不拖慢电脑速度
- 不同系统和硬件,只要安装好 Docker 其他都一样了,一个命令搞定所有
安装 Redis
Docker 官方镜像仓库查找 Redis
:https://hub.docker.com/
一个命令跑起来:docker run -d -p 6379:6379 --name redis redis:latest
命令参考:https://docs.docker.com/engine/reference/commandline/run/
安装 Wordpress
新建 yaml
文件:
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:
在文件存放处打开 cmd
运行:docker-compose up -d
安装 nginx
Nginx 是一款轻量级的 Web 服务器、反向代理服务器,反向代理服务器在客户端和 Web 服务器之间起到中介作用,它将客户端的请求转发到后端服务器,并将响应发送回客户端,同时隐藏了后端服务器的细节。
# 拉取镜像
[root@fantastic-lasers-1 ~]# dokcer pull nginx
# 启动镜像
[root@fantastic-lasers-1 ~]# docker run -d --name nginx01 -p 3344:80 nginx
061756b547f6415e4ca669065ca6a521c50beb04f75fff89259a79f70c5eb54e
[root@fantastic-lasers-1 ~]# docker ps
# 本机测试
# curl:不带任何参数时,curl 就是发出 GET 请求
[root@fantastic-lasers-1 ~]# curl localhost:3344
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>
<p><em>Thank you for using nginx.</em></p>
</body>
</html>
# 进入容器
[root@fantastic-lasers-1 ~]# docker exec -it nginx01 /bin/bash
root@061756b547f6:/# whereis nginx
nginx: /usr/sbin/nginx /usr/lib/nginx /etc/nginx /usr/share/nginx
root@061756b547f6:/# cd /etc/nginx
root@061756b547f6:/etc/nginx# ls
conf.d fastcgi_params mime.types modules nginx.conf scgi_params uwsgi_params
思考问题:我们每次改动 nginx 配置文件,都需要进入容器内部?十分麻烦,我们要是可以在容器外部提供一个映射路径,达到在容器修改文件名,容器内部就可以自动修改?
答案: -v 数据卷
安装 tomcat
docker run -it --rm tomcat:9.0
# 我们之前的启动都是后台,停止了容器之后,容器还是可以查到,但是加上 --rm 之后,容器用完就删除,一般用来测试
[root@fantastic-lasers-1 ~]# docker pull tomcat
# 启动运行
[root@fantastic-lasers-1 ~]# docker run -d -p 3355:8080 --name tomcat01 tomcat
# 测试访问没有问题
# 进入容器
[root@fantastic-lasers-1 ~]# docker exec -it tomcat01 /bin/bash
发现问题:
- linux 命令少了
- 没有 webapps
这是阿里云镜像的原因,默认是最小的镜像,所有不必要的都剔除掉了,只保证最小可运行的环境。
思考问题:我们以后要部署项目,如果每次都要进入容器,是不是十分的麻烦?我们要是可以在容器外部提供一个映射路径,webapps
,我们在外部放置项目,就自动同步到内部就好了!
安装 ES + Kibana
[root@fantastic-lasers-1 ~]# docker pull elasticsearch
[root@fantastic-lasers-1 ~]# docker run -d --name elasticsearch -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" -e ES_JAVA_OPTS="-Xms64m -Xmx512m" elasticsearch
# 查看容器占用内存情况
[root@fantastic-lasers-1 ~]# docker stats
CONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDS
e901ec719765 elasticsearch 0.21% 245.7MiB / 1008MiB 24.38% 906B / 0B 83.4MB / 156kB 36
[root@fantastic-lasers-1 ~]# curl localhost:9200
{
"name" : "025Nxqc",
"cluster_name" : "elasticsearch",
"cluster_uuid" : "tA3Bvd-qTGmfJZI27oZEPg",
"version" : {
"number" : "5.6.12",
"build_hash" : "cfe3d9f",
"build_date" : "2018-09-10T20:12:43.732Z",
"build_snapshot" : false,
"lucene_version" : "6.6.1"
},
"tagline" : "You Know, for Search"
}
思考:使用 Kibana 连接 es
更多相关命令
docker ps
:查看当前运行中的容器docker images
:查看镜像列表docker rm container-id
:删除指定 id 的容器docker stop/start container-id
:停止 / 启动指定 id 的容器docker rmi iamge-id
:删除指定 id 的镜像docker volume ls
:查看 volume 的列表docker network ls
:查看网络列表
制作自己的镜像
使用教程
commit
docker commit # 提交容器成为一个新的副本
# 命令和 git 原理类似
docker commit -m="提交的描述信息" -a="作者" 容器id 目标镜像名:[TAG]
实战测试:
[root@fantastic-lasers-1 ~]# docker run -it -p 8080:8080 tomcat
[root@fantastic-lasers-1 ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS
05c01709e8ce tomcat "catalina.sh run" About a minute ago Up About a minute 0.0.0.0:8080->8080/tcp
[root@fantastic-lasers-1 ~]# docker exec -it 05c01709e8ce /bin/bash
root@05c01709e8ce:/usr/local/tomcat# cp webapps.dist/* webapps
root@05c01709e8ce:/usr/local/tomcat# cd webapps
root@05c01709e8ce:/usr/local/tomcat/webapps# ls
ROOT docs examples host-manager manager
把自己写好的镜像提交上去,以后我们就使用修改过的镜像即可:
[root@fantastic-lasers-1 ~]# docker commit -m="add webapps app" -a="LK" 05c01709e8ce tomcat02:1.0
sha256:d214b44cc1784f4660d7d1b8762e8ebe09122e84b43a59ff0846037003aff86e
[root@fantastic-lasers-1 ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
tomcat02 1.0 d214b44cc178 3 seconds ago 684MB
nginx latest 605c77e624dd 15 months ago 141MB
tomcat latest fb5657adc892 16 months ago 680MB
centos latest 5d0da3dc9764 19 months ago 231MB
elasticsearch latest 5acf0e8da90b 4 years ago 486MB
如果你想要保存当前容器的状态,就可以通过 commit 来提交,获得一个镜像,就好像我们之前学习 VM 时的快照!
Dockerfile
Dockerfile 介绍
Dockerfile 就是构建 docker 镜像的构建文件!命令脚本!通过这个脚本可以生成一个镜像,镜像是一层一层的,脚本就是一个一个的命令,每个命令都是一层!
举个栗子:
[root@fantastic-lasers-1 docker-test-volume]# pwd
/home/docker-test-volume
# 1. 编写 dockerfile
[root@fantastic-lasers-1 docker-test-volume]# vim dockerfile1
FROM centos
VOLUME ["volume01","volume02"] # 匿名挂载
CMD echo "----end----"
CMD /bin/bash
# 2. docker build dockerfile 成为一个镜像
# -t: 为将要构建的镜像命名,注意镜像名前面是不能有斜杠的
# .: 参数表示 Docker 将在当前目录中查找 Dockerfile 文件,以构建新的镜像
[root@fantastic-lasers-1 docker-test-volume]# docker build -f dockerfile1 -t lk/centos .
[+] Building 0.1s (5/5) FINISHED
=> [internal] load build definition from dockerfile1 0.0s
=> => transferring dockerfile: 120B 0.0s
=> [internal] load .dockerignore 0.0s
=> => transferring context: 2B 0.0s
=> [internal] load metadata for docker.io/library/centos:latest 0.0s
=> [1/1] FROM docker.io/library/centos 0.0s
=> exporting to image 0.0s
=> => exporting layers 0.0s
=> => writing image sha256:0282d836c4f5ab8268cf4ebcf33ca7109bd03217f82358c4fa764466139852f2 0.0s
=> => naming to docker.io/library/lk/centos 0.0s
[root@fantastic-lasers-1 docker-test-volume]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
tomcat02 1.0 d214b44cc178 About an hour ago 684MB
nginx latest 605c77e624dd 15 months ago 141MB
tomcat latest fb5657adc892 16 months ago 680MB
mysql latest 3218b38490ce 16 months ago 516MB
lk/centos latest 0282d836c4f5 19 months ago 231MB
centos latest 5d0da3dc9764 19 months ago 231MB
elasticsearch latest 5acf0e8da90b 4 years ago 486MB
# 3. 测试运行:docker run
[root@fantastic-lasers-1 docker-test-volume]# docker run -it 0282d836c4f5 /bin/bash
[root@b02e20395614 /]# ls -l
total 56
lrwxrwxrwx 1 root root 7 Nov 3 2020 bin -> usr/bin
drwxr-xr-x 5 root root 360 Apr 22 08:48 dev
drwxr-xr-x 1 root root 4096 Apr 22 08:47 etc
drwxr-xr-x 2 root root 4096 Nov 3 2020 home
lrwxrwxrwx 1 root root 7 Nov 3 2020 lib -> usr/lib
lrwxrwxrwx 1 root root 9 Nov 3 2020 lib64 -> usr/lib64
drwx------ 2 root root 4096 Sep 15 2021 lost+found
drwxr-xr-x 2 root root 4096 Nov 3 2020 media
drwxr-xr-x 2 root root 4096 Nov 3 2020 mnt
drwxr-xr-x 2 root root 4096 Nov 3 2020 opt
dr-xr-xr-x 138 root root 0 Apr 22 08:47 proc
dr-xr-x--- 2 root root 4096 Sep 15 2021 root
drwxr-xr-x 11 root root 4096 Sep 15 2021 run
lrwxrwxrwx 1 root root 8 Nov 3 2020 sbin -> usr/sbin
drwxr-xr-x 2 root root 4096 Nov 3 2020 srv
dr-xr-xr-x 13 root root 0 Apr 22 08:47 sys
drwxrwxrwt 7 root root 4096 Sep 15 2021 tmp
drwxr-xr-x 12 root root 4096 Sep 15 2021 usr
drwxr-xr-x 20 root root 4096 Sep 15 2021 var
drwxr-xr-x 2 root root 4096 Apr 22 08:47 volume01
drwxr-xr-x 2 root root 4096 Apr 22 08:47 volume02
# 上面两个目录就是我们生成镜像的时候自动挂载的,数据卷目录
# 4. 发布镜像:docker push
注意:很多官方镜像都是基础包,很多功能都没有,我们通常会自己搭建镜像。
Dockerfile 构建过程
Dockerfile 命令:
- 每个保留关键字(指令)都必须是大写字母
- 执行从上到下顺序执行
#
表示注释- 每一个指令都会创建提交一个新的镜像层,并提交!
dockerfile
是面向开发的,我们以后要发布项目,做镜像,就需要编写dockerfile
文件
![k8s记录-Dockerfile详解- 信方- 博客园](pics/631711-20191220153130957-348455400.png)
实战测试
Docker Hub 中 99% 的镜像都是从基础镜像构建过来的 FROM scratch
,然后配置需要的软件和配置来进行构建。
接下来我们创建一个自己的 centos:
# 1. 编写 dockerfile
[root@fantastic-lasers-1 home]# cd dockerfile/
[root@fantastic-lasers-1 dockerfile]# vim mydockerfile-centos
[root@fantastic-lasers-1 dockerfile]# cat mydockerfile-centos
FROM centos
MAINTAINER lockegogo<[email protected]>
ENV MYPATH /usr/local
WORKDIR $MYPATH
RUN yum -y install vim
RUN yum -y install net-tools
EXPOSE 80
CMD echo $MYPATH
CMD echo "----end----"
CMD /bin/bash
# 2. 通过文件构建镜像
[root@fantastic-lasers-1 dockerfile]# docker build -f mydockerfile-centos -t mycentos:0.1 .
# 3. 测试运行
[root@fantastic-lasers-1 dockerfile]# docker run -it mycentos:0.1
SpringBoot 的 web 项目构建 Docker 镜像
- 打包应用:在终端进入项目跟目录,使用 maven 将应用程序打包成可运行的
jar
文件:mvn clean package
,或者直接在 IDEA 界面点击右边的 Maven \(\to\) 项目 \(\to\) 生命周期 \(\to\) clean \(\to\) package,构建成功之后在 target 文件里可以看到 jar 包
# 在本地测试运行
java -jar springboot-03-web-0.0.1-SNAPSHOT.jar
- 编写
Dockerfile
:在项目根目录下创建Dockerfile
文件,该文件用于描述 Docker 镜像的构建过程:
# 基于 alpine 版本的 OpenJDK 19 镜像构建新的镜像
# 注意这里的版本要和项目创建时的版本一致
FROM openjdk:19-jdk-alpine
LABEL authors="Lockegogo"
# 将应用的 jar 包复制到容器中并更名为 app.jar
COPY target/springboot-03-web-0.0.1-SNAPSHOT.jar app.jar
# 暴露 8080 端口,使得其他容器或主机可以连接该端口
EXPOSE 8080
# 使用 java 命令启动应用
ENTRYPOINT ["java", "-jar", "app.jar"]
-
构建镜像:在终端进入项目根目录,并在该目录下执行以下命令构建 Docker 镜像:
docker build -t my-springboot-app .
-t
:用于指定镜像名字为my-springboot-app
.
:最后的句点表示Dockerfile
文件所在的当前目录
-
运行容器:执行以下命令即可启动一个新容器并运行应用程序:
docker run -p 8080:8080 my-springboot-app
-p
:用于将容器内部的 8080 端口映射到主机的 8080 端口上,这样外部就可以通过浏览器访问该应用程序了
目录挂载
现存问题:
- 使用 Docker 运行后,我们改了项目代码不会立刻生效,需要重新
build
和run
,很是麻烦 - 容器里面产生的数据,例如
log
文件,数据库备份文件,容器删除之后就丢失了
我们希望容器之间可以有一个数据共享的技术。Docker 容器中产生的数据,同步到本地。
目录挂载可以解决以上问题:将容器内的目录,挂载到 Linux 上面,实现容器的持久化和同步操作。容器间也是可以数据共享的。
几种挂载方式:
bind mount
:直接把宿主机目录映射到容器内,适合挂代码目录和配置文件。可挂到多个容器上volume
: 由容器创建和管理,创建在宿主机,所以删除容器不会丢失,官方推荐,更高效,Linux 文件系统,适合存储数据库数据。可挂到多个容器上。tmpfs mount
: 适合存储临时文件,存宿主机内存中。不可多容器共享。
文档参考:https://docs.docker.com/storage/
挂载演示:
bind mount
方式用绝对路径-v D:/code:/app
volume
方式,只需要一个名字-v db-data:/app
示例:docker run -p 8080:8080 --name test-hello -v D:/code:/app -d test:v1
注意:挂载后,容器里的代码就会替代你本机的代码了,如果你的代码目录没有
node_modules
目录,你需要在代码目录执行下npm install --registry=https://registry.npm.taobao.org
确保依赖库都已经安装,否则可能会提示 “Error: Cannot find module ‘koa’”
docker run -it -v 主机目录:容器目录
# 将外部的 /home/ceshi 文件夹和容器内部的 home 文件夹链接起来,双向链接
[root@fantastic-lasers-1 /]# docker run -it -v /home/ceshi:/home centos /bin/bash
# 查看是否挂载成功
[root@fantastic-lasers-1 /]# docker inspect
f38a45b6045e
好处:以后修改只需要在本地修改即可,容器内会自动同步!
实战:
[root@fantastic-lasers-1 /]# docker pull mysql
# 运行容器,需要做数据挂载
# 注意安装启动 mysql 是需要配置密码的
# 官方测试:
# -e: 环境配置
$ docker run --name some-mysql -e MYSQL_ROOT_PASSWORD=my-secret-pw -d mysql:tag
# 启动我们的容器
[root@fantastic-lasers-1 /]# docker run -d -p 3310:3306 -v /home/mysql/conf:/etc/mysql/conf.d -v /home/mysql/data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 --name mysql01 mysql
在本地进行连接:Navicat Premium 16
[root@fantastic-lasers-1 home]# ls
ceshi hello.java mysql test.java www
[root@fantastic-lasers-1 home]# cd mysql
[root@fantastic-lasers-1 mysql]# ls
conf data
[root@fantastic-lasers-1 mysql]# cd data/
[root@fantastic-lasers-1 data]# ls
auto.cnf ca.pem ib_buffer_pool #innodb_temp public_key.pem undo_002
binlog.000001 client-cert.pem ibdata1 mysql server-cert.pem
binlog.000002 client-key.pem ib_logfile0 mysql.ibd server-key.pem
binlog.index #ib_16384_0.dblwr ib_logfile1 performance_schema sys
ca-key.pem #ib_16384_1.dblwr ibtmp1 private_key.pem undo_001
# 在本地创建一个数据库 test,然后再在服务器端查看,也可以看到 test 文件夹
[root@fantastic-lasers-1 data]# ls
auto.cnf ca.pem ib_buffer_pool #innodb_temp public_key.pem undo_001
binlog.000001 client-cert.pem ibdata1 mysql server-cert.pem undo_002
binlog.000002 client-key.pem ib_logfile0 mysql.ibd server-key.pem
binlog.index #ib_16384_0.dblwr ib_logfile1 performance_schema sys
ca-key.pem #ib_16384_1.dblwr ibtmp1 private_key.pem test
# 即使把容器删掉,数据也不会丢失!
具名挂载和匿名挂载:
- 匿名挂载:
-v 容器内路径
,不写主机地址,所有 docker 容器内的卷,没有指定目录的情况下都是在/var/lib/docker/volumes/xxxx
,匿名挂载会随机生成一个名字 - 具名挂载:
-v 卷名:容器内路径
,不同于匿名挂载,具名卷需要赋予其一个有意义的名称,其存放在/var/lib/docker/volumes/卷名/_data
中,具名挂载可以方便地找到我们的卷,大多数情况使用
[root@fantastic-lasers-1 data]# docker run -d -P --name nginx02 -v jumping-nginx:/etc/nginx nginx
[root@fantastic-lasers-1 data]# docker volume ls
- 指定路径挂载:
-v /宿主机路径:容器内路径
, 是一种将主机文件系统的目录 (绝对路径)直接挂载到容器中的方式,容器中的修改也会同时影响到主机上的文件系统,因为它们使用的是同一个文件系统。这种挂载方式的优点是,您可以轻松地将您需要的主机文件目录(例如代码或配置文件)与容器分离出来。
数据卷通信:多容器间进行数据共享,–volumes-from
# 运行父容器,也称为数据卷容器
[root@fantastic-lasers-1 data]# docker run -it --name docker01 lk/centos /bin/bash
# 子容器与父容器数据共享
[root@fantastic-lasers-1 data]# docker run -it --name docker02 --volumes-from docker01 lk/centos
只要有一个容器还在用这个数据,这个数据就会一直存在!因为容器间共享的文件是一个双向拷贝的概念。
# 多个 mysql 实现数据共享和同步
[root@fantastic-lasers-1 data]# docker run -d -p 3310:3306 -v /etc/mysql/conf.d -v /var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 --name mysql01 mysql
[root@fantastic-lasers-1 data]# docker run -d -p 3310:3306 -e MYSQL_ROOT_PASSWORD=123456 --name mysql02 --volume-from mysql01 mysql
结论:
- 容器之间配置信息的传递,数据卷容器的生命周期一直持续到没有容器使用为止。
- 但是一旦你持久化到了本地,这个时候,本地的数据是不会删除的!
Docker 网络:多容器通信
理解 Docker0
-
清空所有容器和镜像
-
查看网卡
[root@fantastic-lasers-1 ~]# ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq state UP qlen 1000
link/ether aa:aa:00:18:61:58 brd ff:ff:ff:ff:ff:ff
inet 104.225.155.88/20 brd 104.225.159.255 scope global eth0
valid_lft forever preferred_lft forever
inet6 fe80::a8aa:ff:fe18:6158/64 scope link
valid_lft forever preferred_lft forever
3: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq state UP qlen 1000
link/ether ba:aa:00:18:61:58 brd ff:ff:ff:ff:ff:ff
inet 169.254.254.254/32 brd 169.254.254.254 scope link eth1
valid_lft forever preferred_lft forever
inet6 fe80::b8aa:ff:fe18:6158/64 scope link
valid_lft forever preferred_lft forever
4: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN
link/ether 02:42:75:f5:22:01 brd ff:ff:ff:ff:ff:ff
inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
valid_lft forever preferred_lft forever
inet6 fe80::42:75ff:fef5:2201/64 scope link
valid_lft forever preferred_lft forever
- 思考:docker 是如何处理容器的网络访问的?
[root@fantastic-lasers-1 ~]# docker run -d -P --name tomcat01 tomcat
[root@fantastic-lasers-1 ~]# docker exec -it tomcat01 /bin/bash
[root@fantastic-lasers-1 ~]# apt update
[root@fantastic-lasers-1 ~]# apt install -y net-tools
[root@fantastic-lasers-1 ~]# apt install -y iproute2
[root@fantastic-lasers-1 ~]# apt install -y iputils-ping
# 查看容器的内部网络地址 ip addr
[root@fantastic-lasers-1 ~]# docker exec -it tomcat01 ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
67: eth0@if68: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0
valid_lft forever preferred_lft forever
# 注意看这里的 67: eth0@if68
# 本机可以 ping 通容器内部
[root@fantastic-lasers-1 ~]# ping 172.17.0.2
PING 172.17.0.2 (172.17.0.2) 56(84) bytes of data.
64 bytes from 172.17.0.2: icmp_seq=1 ttl=64 time=0.150 ms
64 bytes from 172.17.0.2: icmp_seq=2 ttl=64 time=0.062 ms
64 bytes from 172.17.0.2: icmp_seq=3 ttl=64 time=0.070 ms
64 bytes from 172.17.0.2: icmp_seq=4 ttl=64 time=0.079 ms
- 启动容器后再次测试:
[root@fantastic-lasers-1 ~]# ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq state UP qlen 1000
link/ether aa:aa:00:18:61:58 brd ff:ff:ff:ff:ff:ff
inet 104.225.155.88/20 brd 104.225.159.255 scope global eth0
valid_lft forever preferred_lft forever
inet6 fe80::a8aa:ff:fe18:6158/64 scope link
valid_lft forever preferred_lft forever
3: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq state UP qlen 1000
link/ether ba:aa:00:18:61:58 brd ff:ff:ff:ff:ff:ff
inet 169.254.254.254/32 brd 169.254.254.254 scope link eth1
valid_lft forever preferred_lft forever
inet6 fe80::b8aa:ff:fe18:6158/64 scope link
valid_lft forever preferred_lft forever
4: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP
link/ether 02:42:75:f5:22:01 brd ff:ff:ff:ff:ff:ff
inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
valid_lft forever preferred_lft forever
inet6 fe80::42:75ff:fef5:2201/64 scope link
valid_lft forever preferred_lft forever
68: vethc19d0bb@if67: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP
link/ether 06:09:0d:16:94:0e brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet6 fe80::409:dff:fe16:940e/64 scope link
valid_lft forever preferred_lft forever
# 注意看这里的 68: vethc19d0bb@if67
原理:
- Docker 使用的是 Linux 的桥接,宿主机中是一个 Docker 容器的网桥 docker0
- Docker 中的所有网络接口都是虚拟的,虚拟的转发效率高
- 只要安装了 Docker,就会有一个网卡 docker0,网卡使用桥接模式,使用的技术是
evth-pair
技术! - 我们每启动一个 docker 容器,docker 就会给容器分配一个 ip
- 每多启动一个容器,主机
ip addr
就会发现多了一对网卡veth
evth-pair
就是一对的虚拟设备接口,他们都是成对出现的,一段连着协议,一段彼此相连。正因为有这个特性,evth-pair
可以充当一个桥梁,连接各种虚拟网络设备。
- 容器之间是可以互相 ping 通的,因为容器公用一个路由器 docker0,所有的容器不指定网络的情况下,都是 docker0 路由的,docker 会给我们的容器分配一个默认的可用 ip
![image-20230423103147499](pics/image-20230423103147499.png)
–link
思考一个问题:我们编写了一个微服务,database url=ip
;项目不重启,数据库 ip 换掉了,我们希望可以处理这个问题,可以使用名字来访问容器?
[root@fantastic-lasers-1 ~]# docker run -d -P --name tomcat02 tomcat
[root@fantastic-lasers-1 ~]# docker exec -it tomcat02 ping tomcat01
ping: tomcat01: Name or service not known
# 可以通过 --link 解决网络连通问题
[root@fantastic-lasers-1 ~]# docker run -d -P --name tomcat03 --link tomcat02 tomcat
[root@fantastic-lasers-1 ~]# docker exec -it tomcat03 ping tomcat02
PING tomcat02 (172.17.0.3) 56(84) bytes of data.
64 bytes from tomcat02 (172.17.0.3): icmp_seq=1 ttl=64 time=0.128 ms
64 bytes from tomcat02 (172.17.0.3): icmp_seq=2 ttl=64 time=0.112 ms
64 bytes from tomcat02 (172.17.0.3): icmp_seq=3 ttl=64 time=0.121 ms
64 bytes from tomcat02 (172.17.0.3): icmp_seq=4 ttl=64 time=0.075 ms
# 反向可以 ping 通吗?不能
# 查看 hosts 配置
[root@fantastic-lasers-1 ~]# docker exec -it tomcat03 cat /etc/hosts
127.0.0.1 localhost
::1 localhost ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
172.17.0.3 tomcat02 2344c7485449
172.17.0.4 6dfba96c8b71
本质探究:–link
就是我们在 hosts 配置中增加了一个 172.17.0.3 tomcat02 2344c7485449 的映射,所以可以通过容器名称 ping 通。
但是现在 Docker 已经不建议使用 –link 了
docker0 问题:不支持容器名连接访问
创建虚拟网络
# 查看所有的 docker 网络
[root@fantastic-lasers-1 ~]# docker network ls
NETWORK ID NAME DRIVER SCOPE
1a5a7a4c35bb bridge bridge local
cbfc17931aaa host host local
d38d5e451f37 none null local
网络模式:
- bridge:桥接模式(默认),自己创建的网络也属于这个模式
- none:不配置网络
- host:和宿主机共享网络
# 直接启动的命令有一个默认操作 --net bridge,这就是 docker0
[root@fantastic-lasers-1 ~]# docker run -d -P --name tomcat01 --net bridge tomcat
# 自定义网络
# 子网地址:--subnet 192.168.0.0/16
# 路由器地址:--gateway 192.168.0.1
[root@fantastic-lasers-1 ~]# docker network create --driver bridge --subnet 192.168.0.0/16 --gateway 192.168.0.1 mynet
[root@fantastic-lasers-1 ~]# docker network ls
NETWORK ID NAME DRIVER SCOPE
1a5a7a4c35bb bridge bridge local
cbfc17931aaa host host local
46800e325ac1 mynet bridge local
d38d5e451f37 none null local
[root@fantastic-lasers-1 ~]# docker network inspect mynet
接下来我们的容器可以放在自己创建的网络当中:
[root@fantastic-lasers-1 ~]# docker run -d -P --name tomcat-net-01 --net mynet tomcat
[root@fantastic-lasers-1 ~]# docker run -d -P --name tomcat-net-02 --net mynet tomcat
# 可以看到多了两个容器:192.168.0.2 | 192.168.0.3
[root@fantastic-lasers-1 ~]# docker network inspect mynet
[
{
"Name": "mynet",
"Id": "46800e325ac169ce2f45ccc6a08bceaf6b02d71a0fe706667f5239191a690522",
"Created": "2023-04-23T11:51:26.548804973+08:00",
"Scope": "local",
"Driver": "bridge",
"EnableIPv6": false,
"IPAM": {
"Driver": "default",
"Options": {},
"Config": [
{
"Subnet": "192.168.0.0/16",
"Gateway": "192.168.0.1"
}
]
},
"Internal": false,
"Attachable": false,
"Ingress": false,
"ConfigFrom": {
"Network": ""
},
"ConfigOnly": false,
"Containers": {
"513faa75d9ccf6c1fbf5fa54fb27199c9b52ea2c5de07035f7dbc3013f197c43": {
"Name": "tomcat-net-01",
"EndpointID": "b8f1bbe2d2bd5c6b961fc26490352d3caad7dd89a0951d1526a1daaea403370c",
"MacAddress": "02:42:c0:a8:00:02",
"IPv4Address": "192.168.0.2/16",
"IPv6Address": ""
},
"b2547e9f4ddaa83e0a6a975c9bab683a139688ef09942e0a058f13b2c2fdad8e": {
"Name": "tomcat-net-02",
"EndpointID": "9b9f58b4e46ac7be406f0678c782a697bfdaf143f74b7ea4fea31da35c1a516b",
"MacAddress": "02:42:c0:a8:00:03",
"IPv4Address": "192.168.0.3/16",
"IPv6Address": ""
}
},
"Options": {},
"Labels": {}
}
]
现在同一网络内的容器就可以互相 ping 通:
# 直接 ping ip,可通
[root@fantastic-lasers-1 ~]# docker exec -it tomcat-net-01 ping 192.168.0.3
PING 192.168.0.3 (192.168.0.3) 56(84) bytes of data.
64 bytes from 192.168.0.3: icmp_seq=1 ttl=64 time=0.135 ms
64 bytes from 192.168.0.3: icmp_seq=2 ttl=64 time=0.096 ms
# ping 名字,也可通
[root@fantastic-lasers-1 ~]# docker exec -it tomcat-net-01 ping tomcat-net-02
PING tomcat-net-02 (192.168.0.3) 56(84) bytes of data.
64 bytes from tomcat-net-02.mynet (192.168.0.3): icmp_seq=1 ttl=64 time=0.053 ms
64 bytes from tomcat-net-02.mynet (192.168.0.3): icmp_seq=2 ttl=64 time=0.104 ms
网络连通
[root@fantastic-lasers-1 ~]# docker network --help
Usage: docker network COMMAND
Manage networks
# 注意看这个 connect:可以将一个容器连接到一个网络
Commands:
connect Connect a container to a network
create Create a network
disconnect Disconnect a container from a network
inspect Display detailed information on one or more networks
ls List networks
prune Remove all unused networks
rm Remove one or more networks
[root@fantastic-lasers-1 ~]# docker network connect --help
Usage: docker network connect [OPTIONS] NETWORK CONTAINER
Connect a container to a network
Options:
--alias strings Add network-scoped alias for the container
--driver-opt strings driver options for the network
--ip string IPv4 address (e.g., "172.30.100.104")
--ip6 string IPv6 address (e.g., "2001:db8::33")
--link list Add link to another container
--link-local-ip strings Add a link-local address for the container
# 测试打通
[root@fantastic-lasers-1 ~]# docker network connect mynet tomcat01
[root@fantastic-lasers-1 ~]# docker network inspect mynet
# 连接之后发生了什么事情:mynet 增加了 tomcat01
"Containers": {
"8ef78a155944c263373776eac077156788617cb4521ea25cf79f11176ebb43db": {
"Name": "tomcat01",
"EndpointID": "b156c6ceb1b53276a0013c1d736c219e9f0b3a9aba84521c1e86bf13a76a2515",
"MacAddress": "02:42:c0:a8:00:04",
"IPv4Address": "192.168.0.4/16",
"IPv6Address": ""
},
}
# 连通之后将 tomcat01 放到 mynet 网络下
# 实现一个容器两个 ip 地址!
# 阿里云:公网 ip | 私网 ip
[root@fantastic-lasers-1 ~]# docker exec -it tomcat01 ping tomcat-net-01
PING tomcat-net-01 (192.168.0.2) 56(84) bytes of data.
64 bytes from tomcat-net-01.mynet (192.168.0.2): icmp_seq=1 ttl=64 time=0.201 ms
64 bytes from tomcat-net-01.mynet (192.168.0.2): icmp_seq=2 ttl=64 time=0.087 ms
结论:假设要跨网络操作别人,就需要使用 docker network connect 连通!
实战:部署 Redis 集群
参考资料:【狂神说 Java】Redis 最新超详细版教程通俗易懂
使用 shell 脚本:
# 创建网卡
docker network create redis --subnet 172.38.0.0/16
# 通过脚本创建六个 redis 配置
for port in $(seq 1 6); \
do \
mkdir -p /mydata/redis/node-${port}/conf
touch /mydata/redis/node-${port}/conf/redis.conf
cat << EOF >/mydata/redis/node-${port}/conf/redis.conf
port 6379
bind 0.0.0.0
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
cluster-announce-ip 172.38.0.1${port}
cluster-announce-port 6379
cluster-announce-bus-port 16379
appendonly yes
EOF
done
docker run -p 637${port}:6379 -p 1637${port}:16379 --name redis-${port} \
-v /mydata/redis/node-${port}/data:/data \
-v /mydata/redis/node-${port}/conf/redis.conf:/etc/redis/redis.conf \
-d --net redis --ip 172.38.0.1${port} redis:5.0.9-alpine3.11 redis-server /etc/redis/redis.conf; \
docker run -p 6371:6379 -p 16371:16379 --name redis-1 \
-v /mydata/redis/node-1/data:/data \
-v /mydata/redis/node-1/conf/redis.conf:/etc/redis/redis.conf \
-d --net redis --ip 172.38.0.11 redis:5.0.9-alpine3.11 redis-server /etc/redis/redis.conf
docker run -p 6376:6379 -p 16376:16379 --name redis-6 \
-v /mydata/redis/node-6/data:/data \
-v /mydata/redis/node-6/conf/redis.conf:/etc/redis/redis.conf \
-d --net redis --ip 172.38.0.16 redis:5.0.9-alpine3.11 redis-server /etc/redis/redis.conf
# 创建集群
/data # redis-cli --cluster create 172.38.0.11:6379 172.38.0.12:6379 172.38.0.13:6379 172.38.0.14:6379 172.38.0.15:6379 172.38.0.16:6379 --cluster-replicas 1
/data # redis-cli -c
127.0.0.1:6379> cluster info
127.0.0.1:6379> cluster info
cluster_state:ok
cluster_slots_assigned:16384
cluster_slots_ok:16384
cluster_slots_pfail:0
cluster_slots_fail:0
cluster_known_nodes:6
cluster_size:3
cluster_current_epoch:6
cluster_my_epoch:1
cluster_stats_messages_ping_sent:59
cluster_stats_messages_pong_sent:58
cluster_stats_messages_sent:117
cluster_stats_messages_ping_received:53
cluster_stats_messages_pong_received:59
cluster_stats_messages_meet_received:5
cluster_stats_messages_received:117
127.0.0.1:6379> cluster nodes
7b2ac2756ee00dcffc60242731bb763bed32a5dd 172.38.0.13:6379@16379 master - 0 1682230088000 3 connected 10923-16383
ece02c3fca507e0fae03085a0454aaca9516ab22 172.38.0.16:6379@16379 slave f93eb8be3ecfef6d3b0fd2e35ed267b15a2a7e13 0 1682230088000 6 connected
42584cb597365412818ddcc4b0924230f3830968 172.38.0.15:6379@16379 slave bb23faab8346e05118ab66e3585ac17468ef6332 0 1682230089718 5 connected
758b99cdd29a00d2fee27b76f7e7160d56cfd699 172.38.0.14:6379@16379 slave 7b2ac2756ee00dcffc60242731bb763bed32a5dd 0 1682230088715 4 connected
f93eb8be3ecfef6d3b0fd2e35ed267b15a2a7e13 172.38.0.12:6379@16379 master - 0 1682230089000 2 connected 5461-10922
bb23faab8346e05118ab66e3585ac17468ef6332 172.38.0.11:6379@16379 myself,master - 0 1682230088000 1 connected 0-5460
# 第三个节点 master
127.0.0.1:6379> set a b
-> Redirected to slot [15495] located at 172.38.0.13:6379
OK
# 在本机把 redis-3 停掉
[root@fantastic-lasers-1 ~]# docker stop redis-3
# 重新 get a 的值:发现现在的机器是 14,而 14 是 13 的从机,这里可以体现高可用
127.0.0.1:6379> get a
-> Redirected to slot [15495] located at 172.38.0.14:6379
"b"
# 13 master,fail -> 14 master
172.38.0.14:6379> cluster nodes
使用 docker 搭建 redis 集群完成!
Docker Swarm
Docker-Compose
如果项目依赖更多的第三方软件,我们需要管理的容器就更加多,每个都要单独配置运行,指定网络。
在这节,我们使用 docker-compose 把项目的多个服务集合到一起,一键运行。
- 安装 Docker Compose
- 如果你是安装的桌面版 Docker,不需要额外安装,已经包含了。
- 如果是没图形界面的服务器版 Docker,你需要单独安装 安装文档
- 运行
docker-compose
检查是否安装成功
- 编写脚本:要把项目依赖的多个服务集合到一起,我们需要编写一个
docker-compose.yml
文件,描述依赖哪些服务。参考文档:https://docs.docker.com/compose/
version: "3.7"
services:
app:
build: ./
ports:
- 80:8080
volumes:
- ./:/app
environment:
- TZ=Asia/Shanghai
redis:
image: redis:5.0.13
volumes:
- redis:/data
environment:
- TZ=Asia/Shanghai
volumes:
redis:
-
跑起来:
- 在
docker-compose.yml
文件所在目录,执行:docker-compose up
- 命令参考:https://docs.docker.com/compose/reference/up/
- 在
-
Docker-Compose 的其他命令:
- 在后台运行只需要加一个 -d 参数
docker-compose up -d
- 查看运行状态:
docker-compose ps
- 停止运行:
docker-compose stop
- 重启:
docker-compose restart
- 重启单个服务:
docker-compose restart service-name
- 进入容器命令行:
docker-compose exec service-name sh
- 查看容器运行 log:
docker-compose logs [service-name]
- 在后台运行只需要加一个 -d 参数
发布和部署
镜像仓库介绍
镜像仓库用来存储我们 build 出来的 “安装包”,Docker 官方提供了一个 镜像库,里面包含了大量镜像,基本各种软件所需依赖都有,要什么直接上去搜索。
我们也可以把自己 build 出来的镜像上传到 docker 提供的镜像库中,方便传播。
当然你也可以搭建自己的私有镜像库,或者使用国内各种大厂提供的镜像托管服务,例如:阿里云、腾讯云
上传我们的镜像
发布到 Docker Hub
-
注册一个账号
-
创建一个镜像库
-
命令行登录账号:
docker login -u username
-
新建一个 tag,名字必须跟你注册账号一样:
docker tag test:v1 username/test:v1
-
推上去:
docker push username/test:v1
-
部署试下:
docker run -dp 8080:8080 username/test:v1
发布到阿里云上
备份和迁移数据
迁移方式介绍
容器中的数据,如果没有用挂载目录,删除容器后就会丢失数据。
- 如果你是用
bind mount
直接把宿主机的目录挂进去容器,那迁移数据很方便,直接复制目录就好了。 - 如果你是用
volume
方式挂载的,由于数据是由容器创建和管理的,需要用特殊的方式把数据弄出来。
备份和导入 Volume 的流程
- 备份:
- 运行一个 ubuntu 的容器,挂载需要备份的 volume 到容器,并且挂载宿主机目录到容器里的备份目录。
- 运行 tar 命令把数据压缩为一个文件
- 把备份文件复制到需要导入的机器
- 导入:
- 运行 ubuntu 容器,挂载容器的 volume,并且挂载宿主机备份文件所在目录到容器里
- 运行 tar 命令解压备份文件到指定目录
备份 MongoDB 数据演示
-
运行一个 mongodb,创建一个名叫
mongo-data
的 volume 指向容器的 /data 目录
docker run -p 27018:27017 --name mongo -v mongo-data:/data -d mongo:4.4
-
运行一个 Ubuntu 的容器,挂载
mongo
容器的所有 volume,映射宿主机的 backup 目录到容器里面的 /backup 目录,然后运行 tar 命令把数据压缩打包
docker run --rm --volumes-from mongo -v d:/backup:/backup ubuntu tar cvf /backup/backup.tar /data/
-
最后你就可以拿着这个
backup.tar
文件去其他地方导入了
恢复 Volume 数据演示
- 运行一个 ubuntu 容器,挂载 mongo 容器的所有 volumes,然后读取 /backup 目录中的备份文件,解压到 /data/ 目录
docker run --rm --volumes-from mongo -v d:/backup:/backup ubuntu bash -c "cd /data/ && tar xvf /backup/backup.tar --strip 1"
- 注意,volumes-from 指定的是容器名字,strip 1 表示解压时去掉前面 1 层目录,因为压缩时包含了绝对路径