接上一篇: 面试官: 你说你精通 Docker, 那你来详细说说 Dockerfile 吧
一, 容器之间通信
1, 单向通信
1.1, 什么意思
MySQL 和 tomcat 是两个独立的容器, 但是 tomcat 需要和 MySQL 通信, 而 MySQL 完全不用和 tomcat 通信, 这就叫容器之间的单向通信.
1.2, 怎么通信
要谈通信, 就需要谈一下 ip, 因为不知道 ip 是无法通信的. 最简单的例子你 jdbc 要连接 MySQL 数据库, 你也需要配置 MySQL 的 ip 地址. 容器之间也不例外, 都是靠虚拟 ip 来完成的.
何为虚拟 ip?
虚拟 ip: 容器创建完成后都会生成一个唯一的 ip, 这个 ip 外界不能直接访问, 他只用于容器之间进行通信交互用. 这就是虚拟 ip.
容器之间的虚拟 ip 是互通的.
通信是什么意思, 靠什么通信我们都知道了, 那还不抓紧实战一把?
1.3, 实战演示
1.3.1, 创建 tomcat 容器
- docker run -d --name mytomcat tomcat
- # --name 指定的名称再 docker ps 里是可以看到的, 最后一列 Name
- docker ps
知识点出现了!!!--name 是神马鬼? 先看如下一个场景
在公司或者你直接买的阿里云数据库 / Redis 等服务为什么给你个数据库域名而不是推荐用 ip? 因为 ip 的话可变, 比如你业务系统写死了 ip, 这时候人家那边内网 ip 变了, 你这所有用这个数据库的业务系统都要跟着改. 用域名的话一劳永逸, 底层 ip 变了后再映射到新域名上就行, 不影响业务系统.
--name 就是给 docker 配置名称来与虚拟 ip 做映射, 因为 ip 老变化, 每次变化的时候其他容器都需要跟着改动才行. 配个名称一劳永逸. 创建容器的时候通过 --name xxx 即可指定.
1.3.2, 创建 MySQL 容器
我也没拉取 MySQL 镜像, 就用 CentOS 模拟一下数据库吧, 主要是看能不能 ping 通, 能 ping 通就代表能通信.
docker run -d --name database -it CentOS /bin/bash
1.3.3, 小试一把
我们需要进入 mytomcat 的容器然后去 ping database 的 ip 看看是否通, 那么容器的虚拟 ip 怎么查呢?
# 这又是一个知识点
docker inspect 容器 id
- # 比如:(9bf58b4014dd 是我们 database 的容器 id)
- docker inspect 9bf58b4014dd
现在知道数据库的 ip 了, 那赶紧进入我们的 mytomcat 的容器去 ping 一波
- docker exec -it mytomcat /bin/bash
- ping 172.17.0.6
完美!
等等, 貌似不是很完美, 我们给数据库指定了名称 database, 那我们赶紧 ping database 试一下. 结果啪啪啪打脸, 完全不通, 那是因为相当于你就起了个名字, 并没有做映射. 那怎么映射呢? mytomcat 启动容器的时候指定一个 --link 参数即可.
- # 强制删除老的
- docker rm -f mytomcat
- # 创建新的容器, 用 --link 指定我们想连的配置的数据库 "域名"
- docker run -d --name mytomcat --link database tomcat
- # 进入 mytomcat 容器
- docker exec -it mytomcat /bin/bash
- # ping
- ping database
这次是真的完美~!
1.4, 总结
容器简单虚拟 ip 是互通的
用 --name 和 --link 可以完成自定义 "域名" 来取代可变化的 ip
2, 双向通信
方式有很多, 一般都采取桥接方式. 由于篇幅过长, 自行 Google 即可. 重点搞懂了容器间的通信是什么意思, 大概怎么做即可. 比如上面的 --link 也是其一做法.
二, 容器间数据共享
1, 场景
需要宿主机和容器之间共享数据的业务场景. 比如 MySQL 的
比如: 集群部署的时候, 我们的应用程序需要部署到 10 个 docker 容器里, 那么比如要想改动一个文件的内容, 就要重新打包然后部署 10 次. 我们可以将我们需要部署的应用程序挂载到宿主机上, 这样改一处就行了. 比如静态 html 文件, 再一个宿主机上启动了 10 个容器, 这时候需求需要改文案 (修改 HTML), 我们需要修改 10 个容器里的 HTML, 过于麻烦, 所以可以把这个 HTML 挂载到宿主机, 容器直接使用挂载到宿主机的文件即可.
再比如: MySQL 的数据目录可配置文件 (一些高级配置或者优化配置啥的肯定要用一份), 这也可以用此场景.
2, 语法
# 语法
docker run -v 宿主机路径: 容器内挂载路径 镜像名
- # 比如如下: 他会把 / home/main/programe 下面的所有目录都挂载到容器的 / usr/local/tomcat/webapps 下
- docker run -v /home/main/programe:/usr/local/tomcat/webapps tomcat
3, 实战
3.1, 准备
在如下目录里创建如下文件, 并写上 Hello Volumn~
/home/main/docker/webapps/volumn-test/index.HTML
3.2, 操作
很简单, 按照上面的语法来就成了, 如下就是将 / home/main/docker/webapps 下的目录挂载到容器内部 / usr/local/tomcat/webapps 的目录下
docker run --name t2 -d -p 8200:8080 -v /home/main/docker/webapps:/usr/local/tomcat/webapps tomcat
3.3, 验证
我们先进入容器
docker exec -it t2 /bin/bash
然后查看 / usr/local/tomcat/webapps 下是否有我们挂载的目录以及文件
- root@4be396ff443b:/usr/local/tomcat/webapps# ls -R volumn-test/
- volumn-test/:
index.HTML
最后我们访问下看看效果
- [root@izm5 volumn-test]# curl 'localhost:8200/volumn-test/index.html'
- Hello Volumn~~
我们修改下宿主机上的 index.HTML 的内容, 修改为 Hello Volumn~~ How are you? 然后再次访问看效果:
这里修改的是宿主机 / home/main/docker/webapps/volumn-test 下的 index.HTML, 为不是容器内部的.
- [root@izm5 volumn-test]# curl 'localhost:8200/volumn-test/index.html'
- Hello Volumn~~ How are you?
很完美, 容器无感知的就生效了.
3.4, 好处
我这是启动了一个容器举例, 如果多启动几个呢? 然后产品要修改文案, 那么你登录每个容器里去修改? 或者重新打包然后重新启动所有容器? 有点小题大做呀, 利用 - v 命令进行挂载实现宿主机和容器的数据共享她不香吗?
4, 新的问题
如果容器过多, 那么每次启动容器都要 - v xxx:xxx, 这也很容易写错啊, 写错一个字母都不行, 还有, 如果宿主机换地址了, 这也需要批量更换容器的 docker run -v 的参数, 机器太多不利于维护.
5, 解决问题
共享容器诞生了!
5.1, 共享容器概念
如果容器太多, 每一次都要写 - v xxx:xxx, 过于复杂, 也容易出错, 这时候可以通过 docker create 创建共享容器, 然后启动的时候通过 --volumes-from 指定创建的共享容器名称即可, 也就是说可以把上面 - v xxx:xxx 这一串统一放到一个地方去管理, 容器启动的时候直接引用这个统一配置即可, 方便统一管理.
5.2, 语法
# 创建共享容器语法, 只是创建不是启动. 最后的 / bin/true 就是一个占位符, 没啥乱用.
docker create --name 共享容器名称 -v 宿主机路径: 容器内挂载路径 镜像名称 /bin/true
# 启动容器的时候通过 --volumes-from 共享容器名称来使用共享容器挂载点
docker run --volumes-from 共享容器名称 --name xxx -d 镜像名称
5.3, 实战
- # 创建共享容器
- docker create --name webpage -v /home/main/docker/webapps:/usr/local/tomcat/webapps tomcat /bin/true
- # 采取共享容器的配置来启动容器
- docker run -p 8300:8080 --volumes-from webpage --name t3 -d tomcat
- # 在启动个
- docker run -p 8400:8080 --volumes-from webpage --name t4 -d tomcat
5.4, 验证 && 好处
验证跟第一种 - v 的方式一样, 修改内容, 容器无感知.
相对于第一种方式的好处是:
不用每次都写 - v xxx:xxx 这一长串不仅令人厌恶还容易出现错误的英文字母.
更改路径, 只修改一处即可.
三, DockerCompose
1, 有什么用
比如我们要部署一个 javaweb 应用, 那一般情况都需要三个容器: nginx 容器, tomcat 容器, MySQL 容器. 这是最基本的, 可能更复杂. 那运维人员每次都需要单独启动这三个容器来支撑我们的 Web 应用吗? 有点复杂了.
docker-compose 就是为了简化这个过程的, 相当于是个脚本, 把这三个容器用脚本统一来管理和启动. 节省运维时间和避免出错率. 也就是说多应用互相协同才能完成一件事的时候, 是很好用的, 否则直接 Dockerfile 就完了.
2, 安装 DockerCompose
基于 Linux 的安装.
参考的官方安装文档: https://docs.docker.com/compose/install/
执行如下两个命令就完事了:
- sudo curl -L "https://github.com/docker/compose/releases/download/1.25.5/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
- sudo chmod +x /usr/local/bin/docker-compose
如果第一个命令特别慢的话可以用如下命令代替, 毕竟是国外网站
> curl -L https://get.daocloud.io/docker/compose/releases/download/1.25.5/docker-compose-`uname -s`-`uname -m`> /usr/local/bin/docker-compose
安装完进行验证:
docker-compose --version
3, 实战感受一下
先来感受一下 docker-compose 的威力, 部署下 WordPress 来玩玩. 不知道 WordPress 是啥的自己百度, 就是一个开源博客.
为什么是部署 WordPress, 因为官方也是 WordPress.... 面向官方文档学习...
官方文档安装 WordPress 的教程: https://docs.docker.com/compose/wordpress/
(1) 建立如下目录
/home/main/docker/WordPress
(2) 建立 docker-compose.YAML 文件
- cd /home/main/docker/WordPress
- vi docker-compose.YAML
(3) 在 docker-compose.YAML 里写上如下内容
- version: '3.3'
- services:
- db:
- image: MySQL:5.7
- volumes:
- - db_data:/var/lib/MySQL
- restart: always
- environment:
- MYSQL_ROOT_PASSWORD: somewordpress
- MYSQL_DATABASE: WordPress
- MYSQL_USER: WordPress
- MYSQL_PASSWORD: WordPress
- WordPress:
- depends_on:
- - db
- image: WordPress:latest
- ports:
- - "8000:80"
- restart: always
- environment:
- WORDPRESS_DB_HOST: db:3306
- WORDPRESS_DB_USER: WordPress
- WORDPRESS_DB_PASSWORD: WordPress
- WORDPRESS_DB_NAME: WordPress
- volumes:
- db_data: {}
看不懂? 正常, 也没关系. 这就是大名鼎鼎的 docker-compose, 就是一个一. YAML 为后缀结尾的脚本文件. 他能自动帮我们部署我们配置的容器, 比如上述有 MySQL 容器和 WordPress 容器. 我们还能看到端口是 8000, 这就够了. 开干!
(4) 执行脚本
- # 先进入你的 docker-compose.YAML 所在的目录
- cd /home/main/docker/WordPress
- # 执行脚本
- docker-compose up -d
(5) 结果分析
- [root@izm5e3qug7oee4q1y4opibz WordPress]# docker-compose up -d
- Creating network "wordpress_default" with the default driver
- Creating volume "wordpress_db_data" with default driver
- Pulling db (MySQL:5.7)...
- 5.7: Pulling from library/MySQL
- afb6ec6fdc1c: Pull complete
- ....
- 0bdc5971ba40: Pull complete
- Digest: sha256:d16d9ef7a4ecb29efcd1ba46d5a82bda3c28bd18c0f1e3b86ba54816211e1ac4
- Status: Downloaded newer image for MySQL:5.7
- Pulling WordPress (WordPress:latest)...
- latest: Pulling from library/WordPress
- afb6ec6fdc1c: Already exists
- 3d895574014b: Pull complete
- ...
- Digest: sha256:0b452b7b45fa770f12e864720abb01bef506f4abe273669402434e94323c97d7
- Status: Downloaded newer image for WordPress:latest
- Creating wordpress_db_1 ... done
- Creating wordpress_wordpress_1 ... done
可以看到 pulling db,pulling WordPress,done. 大概了解到为我们创建了 WordPress 和 WordPress 所需要的 MySQL 数据库. 访问 8000 端口, 大功告成!
4, 和 Dockerfile 区别
Dockerfile 容器间通信需要 --link 或者桥接方式进行, 而 DockerCompose 全自动的呀. 也就是说单容器的话肯定 Dockerfile 了, 但是多容器之间需要交互, 有依赖关系, 那用 DockerCompose 来统一管理那些零散的 Dockerfile 来达到自动构建部署的一体化脚本.
5, 实战
5.1, 需求描述
实战一个 spring boot 的项目, 一个 springboot 的 jar 包依赖 MySQL 数据库. 我们用 docker-compose 完成自动化部署.
5.2, 准备工作
5.2.1, 准备如下文件
- [root@izm5e3qug7oee4q1y4opibz docker-compose-App]# pwd
- /home/main/docker/docker-compose-App
- [root@izm5e3qug7oee4q1y4opibz docker-compose-App]# ls -l
- total 12
- drwxr-xr-x 2 root root 4096 May 24 12:20 App
- drwxr-xr-x 2 root root 4096 May 24 12:20 db
- -rw-r--r-- 1 root root 335 May 24 12:20 docker-compose.YAML
- 5.2.2,App
里面就是我们的 springboot 的 jar 包和制作镜像的 Dockerfile 文件.
- [root@izm5e3qug7oee4q1y4opibz docker-compose-App]# ll App/
- total 23492
- -rw-r--r-- 1 root root 1071 May 24 12:19 application-dev.YAML
- -rw-r--r-- 1 root root 1457 May 24 12:19 application.YAML
- -rw-r--r-- 1 root root 24042957 May 24 12:20 bsbdj.jar
- -rw-r--r-- 1 root root 154 May 24 12:20 Dockerfile
看下 application-dev.YAML 的配置, 主要看数据库配置:
- spring:
- datasource:
- driver-class-name: com.MySQL.jdbc.Driver
- url: jdbc:MySQL://db:3306/bsbdj?useUnicode=true
- username: root
- password: root
- tomcat:
- init-s-q-l: SET NAMES utf8mb4 COLLATE utf8mb4_unicode_ci
- server:
- port: 80
这里我们发现猫腻了, 数据库配置是 db:3306, 这不是平常的 ip / 域名神马的, 这个是 MySQL 容器名称. 所以这个服务依赖 MySQL 服务, 所以我们的主要目的就是利用 DockerCompose 来自动化操作多容器的 Dockerfile 文件来自动部署和初始化 SQL 的操作.
5.2.3,db
jar 包所需要的数据库的 sql 文件和制作镜像的 Dockerfile 文件.
- [root@izm5e3qug7oee4q1y4opibz docker-compose-App]# ll db/
- total 35612
- -rw-r--r-- 1 root root 69 May 24 12:20 Dockerfile
- -rw-r--r-- 1 root root 36460577 May 24 12:20 init-db.sql
5.3, 开始实战
5.3.1,App 的 Dockerfile
- FROM openjdk:8u222-jre
- WORKDIR /usr/local/bsbdj
- ADD bsbdj.jar .
- ADD application.YAML .
- ADD application-dev.YAML .
- EXPOSE 80
- CMD ["java","-jar","bsbdj.jar"]
5.3.2,db 的 Dockerfile
- FROM MySQL:5.7
- WORKDIR /docker-entrypoint-initdb.d
- ADD init-db.sql .
这里有个细节: 为什么是进入 docker-entrypoint-initdb.d 这目录在 ADD sql? 因为这个目录是个后门, 这个目录下的 sql 文件会自动执行. 我咋知道的? 官方告诉我的:
https://hub.docker.com/_/mysql
5.3.3, 最终 Boss:docker-compose.YAML
现在我们应用程序的 Dockerfile 和应用程序所依赖的数据库 Dockerfile 都已就绪. 还剩下最后一个终极 YAML 配置文件
- # 目前最稳定版本: 3.3, 所以 3.3 就行.
- version: '3.3'
- services:
- ## 服务名称叫 db, 还记得我们 application-dev.YAML 的配置吗? 数据库配置的是 db, 对应的就是这里了.
- db:
- # Dockerfile 文件所属的目录.
- build: ./db/
- # always: 宕机自动重启. 牛逼的很.
- restart: always
- # 环境变量, 类似于 - e 参数
- environment:
- MYSQL_ROOT_PASSWORD: root
- # 服务名称叫 App
- App:
- # Dockerfile 文件所属的目录. 若 App 依赖 db, 则需要把 db 服务配置放到 App 的前面.
- build: ./App/
- # 依赖上面的 db service
- depends_on:
- - db
- # 宿主机和容器的端口均为 80. 上面 App 的 Dockerfile 暴露的是 80 端口, 所以这里容器是 80
- ports:
- - "80:80"
- restart: always
5.3.4, 启动
- # -d 代表后台启动
- docker-compose up -d
启动结果:
- [root@izm5e3qug7oee4q1y4opibz docker-compose-App]# docker-compose up -d
- Creating network "docker-compose-app_default" with the default driver
- Building db
- Step 1/3 : FROM MySQL:5.7
- ---> a4fdfd462add
- Step 2/3 : WORKDIR /docker-entrypoint-initdb.d
- ---> Running in d1ff6e4bb5a8
- Removing intermediate container d1ff6e4bb5a8
- ---> d29a05c5bfcb
- Step 3/3 : ADD init-db.sql .
- ---> 6ae6d9eb35ca
- Successfully built 6ae6d9eb35ca
- Successfully tagged docker-compose-app_db:latest
- WARNING: Image for service db was built because it did not already exist. To rebuild this image you must use `docker-compose build` or `docker-compose up --build`.
- Building App
- Step 1/7 : FROM openjdk:8u222-jre
- 8u222-jre: Pulling from library/openjdk
- 9a0b0ce99936: Pull complete
- db3b6004c61a: Pull complete
- f8f075920295: Pull complete
- 4901756f2337: Pull complete
- 9cfcf0e1f584: Pull complete
- d6307286bdcd: Pull complete
- Digest: sha256:3d3df6a0e485f9c38236eaa795fc4d2e8b8d0f9305051c1e4f7fbca71129b06a
- Status: Downloaded newer image for openjdk:8u222-jre
- ---> 25073ded58d2
- Step 2/7 : WORKDIR /usr/local/bsbdj
- ---> Running in df4a4c352e71
- Removing intermediate container df4a4c352e71
- ---> 0d88b2f13319
- Step 3/7 : ADD bsbdj.jar .
- ---> aabaa119855d
- Step 4/7 : ADD application.YAML .
- ---> 7e1f7b4614cc
- Step 5/7 : ADD application-dev.YAML .
- ---> a8d36115592f
- Step 6/7 : EXPOSE 80
- ---> Running in 26b44c9d57ef
- Removing intermediate container 26b44c9d57ef
- ---> fd36f3cdd115
- Step 7/7 : CMD ["java","-jar","bsbdj.jar"]
- ---> Running in 64bdeff2f1ce
- Removing intermediate container 64bdeff2f1ce
- ---> 77d18bae9bbc
- Successfully built 77d18bae9bbc
- Successfully tagged docker-compose-app_app:latest
- Creating docker-compose-app_db_1 ... done
- Creating docker-compose-app_app_1 ... done
可以看到先为我们构建了 MySQL 的镜像然后又构建了 bsbdj.jar 的镜像. 最后执行了 CMD ["java","-jar","bsbdj.jar"], 这些过程全自动化.
查看容器
docker-compose ps
结果:
- [root@izm5e3qug7oee4q1y4opibz docker-compose-App]# docker-compose ps
- Name Command State Ports
- ------------------------------------------------------------------------------------
- docker-compose-app_app_1 java -jar bsbdj.jar Up 0.0.0.0:80->80/tcp
- docker-compose-app_db_1 docker-entrypoint.sh mysqld Up 3306/tcp, 33060/tcp
然后访问 http://ip:80 即可看到效果.
6, 补充
docker-compose 其他命令可以用 docker-compose --help 查看. 再说下 docker-compose 和 Dockerfile 区别, 可以粗糙理解成 Dockerfile 是针对单容器的脚本, docker-compose 是针对多 Dockerfile 的自动化脚本, 他帮我们处理容器之间的依赖关系和其他需要人为干涉的操作.
四, 上期答疑
上期回顾: 面试官: 你说你精通 Docker, 那你来详细说说 Dockerfile 吧
1.ADD 命令能否解压 zip 格式? 多模块该怎么创建镜像?
ADD 命令不能解压 zip, 亲测. 能解压: tar, gzip, bzip2, etc.
多模块恰巧是今天的主角 docker-compose, 可以为每一个模块都书写一个 Dockerfile, 然后用 docker-compose 去管理这些带有依赖关系的模块.
如果是集群的话, 首选 k8s,docker warm 什么的, 毕竟 docker-compose 致命缺点仅限在单机上.
2. 有了 docker commit 为啥还要 Dockerfile?
docker commit 都是黑箱操作, 过一段时间后自己都不知道这个镜像是怎么做出来的, 都安装了什么. 但是使用 Dockerfile 构建的镜像, 我们能清楚的知道都有哪一层, 每层是干嘛的, 修改也方便, 易于维护.
来源: https://www.cnblogs.com/javazhiyin/p/12978993.html