Docker 容器
初识 Docker
大型项目组件较多,运行环境也较为复杂,部署时会碰到一些问题:
- 依赖关系复杂,容易出现兼容性问题
- 开发、测试、生产环境有差异
比如Dubbo mysql node redis 等依赖。
那么 docker 是怎么解决这些问题的呢?
- 将应用的Libs(函数库)、Deps(依赖)、配置与应用一起打包
- 将每个应用放到一个隔离容器去运行,避免互相干扰
但是打包也是基于当前操作系统打包的,在 ubuntu系统打包是不能放到 centos系统运行的。下面我们就了解一下操作系统。
linux 的内核都是一样的,不同的是系统应用层是不同的。
内核的作用:负责与计算机硬件的交互,比如调用 CPU、调用内存,读取文件等(提供操作硬件指令)。
系统应用做了什么呢?是将封装的内核指令作为函数,便于程序员调用,相当于二次封装。
Docker如何解决不同系统环境的问题?
Docker将用户程序与所需要调用的系统(比如Ubuntu)函数库一起打包
Docker运行到不同操作系统时,直接基于打包的库函数,借助于操作系统的Linux内核来运行
总结:
Docker如何解决大型项目依赖关系复杂,不同组件依赖的兼容性问题?
Docker允许开发中将应用、依赖、函数库、配置一起打包,形成可移植镜像
Docker应用运行在容器中,使用沙箱机制,相互隔离
Docker如何解决开发、测试、生产环境有差异的问题
Docker镜像中包含完整运行环境,包括系统函数库,仅依赖系统的Linux内核,因此可以在任意Linux操作系统上运行
Docker是一个快速交付应用、运行应用的技术:
可以将程序及其依赖、运行环境一起打包为一个镜像,可以迁移到任意Linux操作系统 运行时利用沙箱机制形成隔离容器,各个应用互不干扰启动、移除都可以通过一行命令完成,方便快捷。
Docker 和 虚拟机的区别
两者差异
虚拟机(virtual machine)是在操作系统中模拟硬件设备,然后运行另一个操作系统,比如在 Windows 系统里面运行 Ubuntu 系统,这样就可以运行任意的Ubuntu应用了。
Docker仅仅是封装函数库,并没有模拟完整的操作系统,如图:
- docker是一个系统进程;虚拟机是在操作系统中的操作系统
- docker体积小、启动速度快、性能好;虚拟机体积大、启动速度慢、性能一般
对比结果
| 特性 | Docker | 虚拟机 |
|---|---|---|
| 性能 | 接近原生 | 性能较差 |
| 硬盘占用 | 一般为 MB | 一般为 GB |
| 启动 | 秒级 | 分钟级 |
Docker 架构
镜像和容器
镜像(Image):Docker将应用程序及其所需的依赖、函数库、环境、配置等文件打包在一起,称为镜像。
容器(Container):镜像中的应用程序运行后形成的进程就是容器,只是Docker会给容器做隔离,对外不可见。
容器在运行过程中,是不能向镜像中写入的,镜像是只读的。
那是怎么写数据的呢?
是将镜像文件拷贝到自己独立的系统中,形成隔离性。
Docker和DockerHub
DockerHub:DockerHub是一个Docker镜像的托管平台。这样的平台称为Docker Registry。
国内也有类似于DockerHub 的公开服务,比如 网易云镜像服务、阿里云镜像库等。
docker架构
Docker是一个CS架构的程序,由两部分组成:
- 服务端(server):Docker守护进程,负责处理Docker指令,管理镜像、容器等
- 客户端(client):通过命令或RestAPI向Docker服务端发送指令。可以在本地或远程向服务端发送指令。
镜像:
将应用程序及其依赖、环境、配置打包在一起
容器:
镜像运行起来就是容器,一个镜像可以运行多个容器
Docker结构:
- 服务端:接收命令或远程请求,操作镜像或容器
- 客户端:发送命令或者请求到Docker服务端
DockerHub:
一个镜像托管的服务器,类似的还有阿里云镜像服务,统称为DockerRegistry
安装 Docker
查看 linux 系统内核,要求内核版本不低于 3.10
root@iZ2ze8t8pzxr8it3f5rm9sZ:~# uname -r
# 5.15.0-71-generic
拥有最新 LTS 版本 (Ubuntu Jammy Jellyfish 22.04) 或当前非 LTS 版本 (Ubuntu Mantic Minotaur 23.10) 的 64 位版本。
root@iZ2ze8t8pzxr8it3f5rm9sZ:~# lsb_release -a
LSB Version: core-11.1.0ubuntu4-noarch:security-11.1.0ubuntu4-noarch
Distributor ID: Ubuntu
Description: Ubuntu 22.04.2 LTS
Release: 22.04
Codename: jammy
root@iZ2ze8t8pzxr8it3f5rm9sZ:~#
Docker 分为 CE 和 EE 两大版本。CE 即社区版(免费,支持周期 7 个月),EE 即企业版,强调安全,付费使用,支持周期 24 个月。
Docker CE 分为 stable test 和 nightly 三个更新频道。
官方网站上有各种环境下的 安装指南
卸载(可选)
如果之前安装过旧版本的Docker,可以使用下面命令卸载:
yum remove docker \
docker-client \
docker-client-latest \
docker-common \
docker-latest \
docker-latest-logrotate \
docker-logrotate \
docker-selinux \
docker-engine-selinux \
docker-engine \
docker-ce
安装 docker
- 设置 Docker 的存储库 apt
sudo apt update
sudo apt install ca-certificates curl
- 在 Linux 系统中用来创建目录 /etc/apt/keyrings,并设置目录的权限为 0755,用于存放软件源的 GPG 密钥
sudo install -m 0755 -d /etc/apt/keyrings
- 下载 Docker 官方的 GPG 密钥,并保存到 /etc/apt/keyrings/docker.asc 文件中
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
- 将 /etc/apt/keyrings/docker.asc 文件的权限设置为所有用户可读。
sudo chmod a+r /etc/apt/keyrings/docker.asc
- 将 Docker 存储库的源地址添加到 /etc/apt/sources.list.d/docker.list 文件中。这个命令会根据系统的架构和版本自动选择适用的 Docker 存储库地址。
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \
$(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt update
- 安装 Docker----用于在 Ubuntu 上安装 Docker CE(社区版)、Docker CE CLI(命令行界面)、containerd.io(容器运行时)、docker-buildx-plugin(用于构建多平台镜像的插件)和 docker-compose-plugin(用于管理多容器应用的插件)
sudo apt install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
运行
machine:~/桌面$ sudo docker run hello-world
Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
c1ec31eb5944: Pull complete
Digest: sha256:53641cd209a4fecfc68e11a99871ce8c6920b2e7502df0a20672c6fccc73a7c6
Status: Downloaded newer image for hello-world:latest
Hello from Docker!
启动docker
systemctl start docker # 启动docker服务
systemctl stop docker # 停止docker服务
systemctl restart docker # 重启docker服务
docker -v # 查看docker版本
配置镜像加速 阿里云
# 创建目录以存放 Docker 的配置文件
sudo mkdir -p /etc/docker
# 使用 tee 命令创建并写入 Docker 的配置文件 /etc/docker/daemon.json
# 配置文件中添加了 registry-mirrors 配置项,将阿里云的镜像加速器地址添加进去
sudo tee /etc/docker/daemon.json <<-'EOF'
{
"registry-mirrors": ["https://crhe8igk.mirror.aliyuncs.com"]
}
EOF
# 重新加载 systemd 的配置
sudo systemctl daemon-reload
# 重启 Docker 服务,使配置生效
sudo systemctl restart docker
Docker 基本操作
- 镜像名称一般分两部分组成:[repository]:[tag]。
- 在没有指定tag时,默认是latest,代表最新版本的镜像
命令
docker build # 构建镜像
docker push # 推送镜像到服务
docker pull # 从服务拉取镜像
docker save # 保存镜像为一个压缩包
docker load # 加载压缩包为镜像
docker images # 查看镜像
docker rmi # 删除镜像
拉取 nginx 镜像
拉取 nginx
sudo docker pull nginx
查看所有镜像信息
sudo docker images
将镜像打包导出
docker save -o 镜像名.tar 镜像名:版本
sudo docker save -o nginx.tar nginx:latest
将镜像导入
sudo docker load -i nginx.tar
容器相关命令
docker run # 用于创建并启动一个新的容器实例。
docker pause # 暂停容器
docker unpause # 回复暂停的容器
docker stop # 停止容器
docker start # 重新允许容器
docker exec # 进入容器执行命令
docker logs 容器名称 # 查看容器运行日志 -f 表示跟踪
docker ps # 查看所有运行的容器及状态 -a 查看全部的容器
docker rm # 删除指定容器
nginx 容器
步骤一:去docker hub查看Nginx的容器运行命令
# docker run --name containerName -p 80:80 -d nginx
sudo docker run --name nm -p 80:80 -d nginx
命令解读:
- docker run :创建并运行一个容器
- --name : 给容器起一个名字,比如叫做mn
- -p :将宿主机端口与容器端口映射,冒号左侧是宿主机端口,右侧是容器端口
- -d:后台运行容器
- nginx:镜像名称,例如nginx
进入容器
docker exec -it 容器名称 bash
命令解读:
- docker exec :进入容器内部,执行一个命令
- -it : 给当前进入的容器创建一个标准输入、输出终端,允许我们与容器交互
- mn :要进入的容器的名称
- bash:进入容器后执行的命令,bash是一个linux终端交互命令
命令是docker exec -it [容器名] [要执行的命令]
exec命令可以进入容器修改文件,但是在容器内修改文件是不推荐的
redis 容器
sudo docker run --name redis-nm -p 6379:6379 -d redis --save 60 1 --loglevel warning
docker run: 这是运行 Docker 容器的命令。
- --name redis-nm: 这个选项为容器指定一个名称,这里是 redis-nm。通过这个名称,你可以在后续的操作中引用这个容器。
- -p 6379:6379: 这个选项指定了容器的端口映射规则。左边的 6379 是主机(即宿主机)的端口,右边的 6379 是容器内 Redis 服务器监听的端口。这表示宿主机上的 6379 端口会被映射到容器内的 6379 端口,以便可以从外部访问 Redis 服务器。
- -d: 这个选项表示在后台运行容器。
- redis: 这是要运行的 Docker 镜像的名称,表示使用官方的 Redis 镜像。 --save 60 1: 这是 Redis 服务器的配置选项之一,用于指定在多长时间内至少执行多少次写入操作才会执行数据保存操作。这里的意思是在 60 秒内,至少有 1 次写入操作时执行保存操作。 --loglevel warning: 这是设置 Redis 日志级别的选项,表示只记录警告及以上级别的日志信息。
进入容器并打开redis
sudo docker exec -it redis-nm redis-cli
数据卷
容器与数据耦合的问题
数据卷(volume)是一个虚拟目录,指向宿主机文件系统中的某个目录。
数据卷是一个概念真实是在宿主机的文件中,容器通过挂载数据卷实现关联到宿主机目录。实现文件互通。实现复用等。
数据卷操作的基本语法如下:
docker volume命令是数据卷操作,根据命令后跟随的command来确定下一步的操作:
- create 创建一个volume
- inspect 显示一个或多个volume的信息,详细信息以及位置
- ls 列出所有的volume
- prune 删除未使用的volume
- rm 删除一个或多个指定的volume
数据卷的作用:
将容器与数据分离,解耦合,方便操作容器内数据,保证数据安全
挂载数据卷
我们在创建容器时,可以通过 -v 参数来挂载一个数据卷到某个容器目录
通过 -v 实现挂载,html是数据卷的名字。 :ro 表示不允许容器修改
sudo docker run --name nm -p 80:80 -v html:/usr/share/nginx/html:ro -d nginx
- docker run :就是创建并运行容器
- -- name mn :给容器起个名字叫mn
- -v html:/root/htm :把html数据卷挂载到容器内的/root/html这个目录中
- -p 8080:80 :把宿主机的8080端口映射到容器内的80端口
- nginx :镜像名称
在执行创建容器时,数据卷不存在会自动创建。
mysql 目录挂载
提示:目录挂载与数据卷挂载的语法是类似的:
-v [宿主机目录]:[容器内目录]
-v [宿主机文件]:[容器内文件]
命令解读:
- restart unless-stopped 容器异常关闭会自动重启
- p 映射3306端口
- -v /var/mysql/log:/var/log/mysql / 映射日志文件夹
- -v /var/mysql/data:/var/lib/mysql / 映射数据文件夹
- -v /var/mysql/config:/etc/mysql/conf.d / 映射单个配置文件
sudo docker run /
--name mysql-nm /
-p 3306:3306 /
--restart unless-stopped /
-v /var/mysql/log:/var/log/mysql /
-v /var/mysql/data:/var/lib/mysql /
-v /var/mysql/config:/etc/my.cnf /
-e MYSQL_ROOT_PASSWORD=1234 /
-d mysql
配置文件必须配置
[mysqld]
port=3306
basedir=/var/lib/mysql
datadir=/var/lib/mysql/data
slow_query_log = ON
slow_query_log_file = /var/log/mysql/slow.log
long_query_time = 5
数据卷挂载与目录直接挂载的对比
数据卷挂载耦合度低,由docker来管理目录,但是目录较深,不好找
目录挂载耦合度高,需要我们自己管理目录,不过目录容易寻找查看
自定义镜像
镜像结构
入口(Entrypoint)
镜像运行入口,一般是程序启动的脚本和参数
层( Layer )
在BaseImage基础上添加安装包、依赖、配置等,每次操作都形成新的一层。
基础镜像(BaseImage)
应用依赖的系统函数库、环境、配置、文件等
镜像是分层结构,每一层称为一个Layer
- BaseImage层:包含基本的系统函数库、环境变量、文件系统
- Entrypoint:入口,是镜像中应用启动的命令
- 其它:在BaseImage基础上添加依赖、安装程序、完成整个应用的安装和配置
什么是Dockerfile
Dockerfile就是一个文本文件,其中包含一个个的指令(Instruction),用指令来说明要执行什么操作来构建镜像。每一个指令都会形成一层Layer。
| 指令 | 说明 | 示例 |
|---|---|---|
| FROM | 指定基础镜像 | FROM centos:6 |
| ENV | 设置环境变量,可在后面指令使用 | ENV key value |
| COPY | 拷贝本地文件到镜像的指定目录 | COPY ./mysql-5.7.rpm /tmp |
| RUN | 执行Linux的shell命令,一般是安装过程的命令 | RUN yum install gcc |
| EXPOSE | 指定容器运行时监听的端口,是给镜像使用者看的 | EXPOSE 8080 |
| ENTRYPOINT | 镜像中应用的启动命令,容器运行时调用 | ENTRYPOINT java -jar xx.jar |
更新详细语法说明,请参考官网文档: docs.docker.com/engine/refe…
创建脚本文件---并准备好文件
# 指定基础镜像
FROM ubuntu:16.04
# 配置环境变量,JDK的安装目录
ENV JAVA_DIR=/usr/local
# 拷贝jdk和java项目的包
COPY ./jdk8.tar.gz $JAVA_DIR/
COPY ./docker-demo.jar /tmp/app.jar
# 安装JDK
RUN cd $JAVA_DIR \
&& tar -xf ./jdk8.tar.gz \
&& mv ./jdk1.8.0_144 ./java8
# 配置环境变量
ENV JAVA_HOME=$JAVA_DIR/java8
ENV PATH=$PATH:$JAVA_HOME/bin
# 暴露端口
EXPOSE 8090
# 入口,java项目的启动命令
ENTRYPOINT java -jar /tmp/app.jar
在当前目录进行构建
- . 表示在当前目录
docker build -t 构建后的名称 .
快速构建,无需配置环境 基于 openjdk:8
# 指定基础镜像
FROM openjdk:8
COPY ./docker-demo.jar /tmp/app.jar
# 暴露端口
EXPOSE 8090
# 入口,java项目的启动命令
ENTRYPOINT java -jar /tmp/app.jar
- Dockerfile的本质是一个文件,通过指令描述镜像的构建过程
- Dockerfile的第一行必须是FROM,从一个基础镜像来构建
- 基础镜像可以是基本操作系统,如Ubuntu。也可以是其他人制作好的镜像,例如:java:8-alpine
DockerCompose
什么是DockerCompose
Docker Compose可以基于Compose文件帮我们快速的部署分布式应用,而无需手动一个个创建和运行容器! Compose文件是一个文本文件,通过指令定义集群中的每个容器如何运行。
version: "3.8"services:
mysql:
image: mysql:5.7.25
environment:
MYSQL_ROOT_PASSWORD: 123
volumes:
- "/tmp/mysql/data:/var/lib/mysql"
- "/tmp/mysql/conf/hmy.cnf:/etc/mysql/conf.d/hmy.cnf"
web:
build: .
ports:
- "8090:8090"
DockerCompose的详细语法参考官网:docs.docker.com/compose/com…
安装 DockerCompose
前提是需要安装 docker
这个我们前面已经安装了
sudo apt install docker-compose-plugin
sudo apt install docker-compose
DockerCompose有什么作用?
帮助我们快速部署分布式应用,无需一个个微服务去构建镜像和部署。
DockerCompose-部署微服务
实现思路如下:
- 查看课前资料提供的cloud-demo文件夹,里面已经编写好了docker-compose文件
- 修改自己的cloud-demo项目,将数据库、nacos地址都命名为docker-compose中的服务名
- 使用maven打包工具,将项目中的每个微服务都打包为app.jar
- 将打包好的app.jar拷贝到cloud-demo中的每一个对应的子目录中
- 将cloud-demo上传至虚拟机,利用 docker-compose up -d 来部署
docker-compose yml文件
version: "3.2" # 使用的 Docker Compose 版本
services: # 定义多个 Docker 服务
nacos: # Nacos 服务的配置
image: nacos/nacos-server # 使用的镜像
environment: # 定义环境变量
MODE: standalone # 设置 Nacos 的模式为独立模式
ports: # 定义端口映射
- "8848:8848" # 将宿主机的 8848 端口映射到容器的 8848 端口
mysql: # MySQL 服务的配置
image: mysql:5.7.25 # 使用的 MySQL 5.7.25 版本的镜像
environment: # 定义环境变量
MYSQL_ROOT_PASSWORD: 123 # 设置 MySQL 的 root 密码
volumes: # 定义数据卷挂载
- "$PWD/mysql/data:/var/lib/mysql" # 将宿主机的 mysql/data 目录挂载到容器的 /var/lib/mysql 目录,用于存放 MySQL 数据
- "$PWD/mysql/conf:/etc/mysql/conf.d/" # 将宿主机的 mysql/conf 目录挂载到容器的 /etc/mysql/conf.d/ 目录,用于存放 MySQL 配置文件
userservice: # 用户服务的配置
build: ./user-service # 使用 user-service 目录下的 Dockerfile 构建镜像
orderservice: # 订单服务的配置
build: ./order-service # 使用 order-service 目录下的 Dockerfile 构建镜像
gateway: # 网关服务的配置
build: ./gateway # 使用 gateway 目录下的 Dockerfile 构建镜像
ports: # 定义端口映射
- "10010:10010" # 将宿主机的 10010 端口映射到容器的 10010 端口
注意事项:
当你在 Docker 中使用容器名称来指定服务之间的通信时,其背后的原理是 Docker 默认为创建的容器设置了一个本地 DNS 服务。这个 DNS 服务允许你通过容器名称来解析容器之间的通信地址,而无需了解容器的 IP 地址。
所以下面我们在程序中连接 mysql 和 nacos,docker-compose文件中定义的。
spring:
datasource:
url: jdbc:mysql://mysql:3306/mydatabase
username: root
password: 123
spring:
nacos:
serverAddr: nacos:8848
在根目录运行
# 会以后台模式启动 Docker Compose 配置文件中定义的所有服务。
sudo docker-compose up -d
# 追踪日志
sudo docker-compose logs -f
# 重启 gateway
sudo docker-compose restart gateway
镜像仓库
除了使用公开仓库外,用户还可以在本地搭建私有 Docker Registry。企业自己的镜像最好是采用私有Docker Registry来实现。
简化版镜像仓库
Docker官方的Docker Registry是一个基础版本的Docker镜像仓库,具备仓库管理的完整功能,但是没有图形化界面。
搭建方式比较简单,命令如下:
docker run -d \
--restart=always \
--name registry \
-p 5000:5000 \
-v registry-data:/var/lib/registry \
registry
命令中挂载了一个数据卷registry-data到容器内的/var/lib/registry 目录,这是私有镜像库存放数据的目录。
访问http://YourIp:5000/v2/_catalog 可以查看当前私有镜像服务中包含的镜像
带有图形化界面版本
使用DockerCompose部署带有图象界面的DockerRegistry,命令如下:
version: '3.0'
services:
registry:
image: registry
volumes:
- ./registry-data:/var/lib/registry
ui:
image: joxit/docker-registry-ui:static
ports:
- 8080:80
environment:
- REGISTRY_TITLE=传智教育私有仓库
- REGISTRY_URL=http://registry:5000
depends_on:
- registry
配置Docker信任地址
我们的私服采用的是http协议,默认不被Docker信任,所以需要做一个配置:
# 打开要修改的文件
vi /etc/docker/daemon.json
# 添加内容:地址使用本机的地址,端口请与上面的配置一样
"insecure-registries":["http://192.168.150.101:8080"]
# 重加载
systemctl daemon-reload
# 重启docker
systemctl restart docker
最后执行
sudo docker-compose up -d
在私有镜像仓库推送或拉取镜像
推送镜像到私有镜像服务必须先tag,步骤如下:
- 重新tag本地镜像,名称前缀为私有仓库的地址:192.168.80.137:8080
sudo docker tag nginx:latest 192.168.80.137:8080/nginx:1.0
- 推送镜像
docker push 192.168.80.137:8080/nginx:1.0
- 拉取镜像
docker pull 192.168.80.137:8080/nginx:1.0
总结
- 推送本地镜像到仓库前都必须重命名(docker tag)镜像,以镜像仓库地址为前缀
- 镜像仓库推送前需要把仓库地址配置到docker服务的daemon.json文件中,被docker信任
- 推送使用docker push命令
- 拉取使用docker pull命令