docker


docker

英文文档:https://docs.docker.com/

docker中文文档:https://gitee.com/docker_practice/docker_practice#https://gitee.com/link?target=https%3A%2F%2Fgithub.com%2Fyeasy%2Fdocker_practice

一 docker的定义

docker就是一种容器应用,用于更好地发布应用。docker可以直接装一个个的软件服务。当我们需要安装redis,MySQL时我们需要下载很多的环境,这会非常的繁琐。并且,当我们想将软件给其他人时,往往会出现“这段代码在我的机器上没问题啊”的问题。docker就很好地解决了这个问题,他将你的应用和你的环境做一个打包,就像一个个的集装箱一样,将各个应用进行隔离,这样就可以运行你的应用而不需要安装环境了。

当我们把应用和容器打包到一起时,我们称之为镜像

更高效的利用系统资源

由于容器不需要进行硬件虚拟以及运行完整操作系统等额外开销,Docker 对系统资源的利用率更高。无论是应用执行速度、内存损耗或者文件存储速度,都要比传统虚拟机技术更高效。因此,相比虚拟机技术,一个相同配置的主机,往往可以运行更多数量的应用。

更快速的启动时间

传统的虚拟机技术启动应用服务往往需要数分钟,而 Docker 容器应用,由于直接运行于宿主内核,无需启动完整的操作系统,因此可以做到秒级、甚至毫秒级的启动时间。大大的节约了开发、测试、部署的时间。

一致的运行环境

开发过程中一个常见的问题是环境一致性问题。由于开发环境、测试环境、生产环境不一致,导致有些 bug 并未在开发过程中被发现。而 Docker 的镜像提供了除内核外完整的运行时环境,确保了应用运行环境一致性,从而不会再出现 「这段代码在我机器上没问题啊」 这类问题。

持续交付和部署

对开发和运维(DevOps (opens new window))人员来说,最希望的就是一次创建或配置,可以在任意地方正常运行。

使用 Docker 可以通过定制应用镜像来实现持续集成、持续交付、部署。开发人员可以通过 Dockerfile 来进行镜像构建,并结合 持续集成(Continuous Integration) (opens new window)系统进行集成测试,而运维人员则可以直接在生产环境中快速部署该镜像,甚至结合 持续部署(Continuous Delivery/Deployment) (opens new window)系统进行自动部署。

而且使用 Dockerfile 使镜像构建透明化,不仅仅开发团队可以理解应用运行环境,也方便运维团队理解应用运行所需条件,帮助更好的生产环境中部署该镜像。

更轻松的迁移

由于 Docker 确保了执行环境的一致性,使得应用的迁移更加容易。Docker 可以在很多平台上运行,无论是物理机、虚拟机、公有云、私有云,甚至是笔记本,其运行结果是一致的。因此用户可以很轻易的将在一个平台上运行的应用,迁移到另一个平台上,而不用担心运行环境的变化导致应用无法正常运行的情况。

更轻松的维护和扩展

Docker 使用的分层存储以及镜像的技术,使得应用重复部分的复用更为容易,也使得应用的维护更新更加简单,基于基础镜像进一步扩展镜像也变得非常简单。此外,Docker 团队同各个开源项目团队一起维护了一大批高质量的 官方镜像 (opens new window),既可以直接在生产环境使用,又可以作为基础进一步定制,大大的降低了应用服务的镜像制作成本

对比传统虚拟机

特性 容器 虚拟机
启动 秒级 分钟级
硬盘使用 一般为 MB 一般为 GB
性能 接近原生 弱于
系统支持量 单机支持上千个容器 一般几十个

二 docker安装

https://docs.docker.com/engine/install/centos/#install-using-the-convenience-script

只有centos7+才能安装docker

docker安装方式有很多,推荐使用脚本安装。可以在任意Linux系统上安装

首先下载docker安装的脚本

curl -fsSL get.docker.com -o get-docker.sh

通过下载的脚本来下载docker引擎,推荐使用阿里云镜像

sh get-docker.sh --mirror Aliyun

启动docker服务

systemctl enable docker #将docker加入开机自启动的列表
systemctl start docker

推荐将当前用户加入docker组

创建docker组

sudo groupadd docker

将当前用户加入docker组

sudo usermod -aG docker $USER

测试docker安装是否正确

docker version
docker info

三 docker的核心概念

镜像 Image

一个镜像就代表一个软件。

容器 Container

一个镜像运行一次就会生成一个容器。容器就是一个运行的软件服务。

举例:我可以在一台电脑中开多个Chrome浏览器。

仓库 docker repository

docker在全球范围内维护了一个镜像仓库。类似于maven

远程仓库:docker hub

https://registry.hub.docker.com/_/tomcat?tab=tags

本地仓库:用来存储在使用docker过程中的相关镜像

四 配置阿里云镜像加速

阿里云会为每个开发者分配一个单独的镜像

https://cr.console.aliyun.com/cn-hangzhou/instances/mirrors

sudo mkdir -p /etc/docker
sudo tee /etc/docker/daemon.json <<-'EOF'
{
  "registry-mirrors": ["自己的url"]
}
EOF
sudo systemctl daemon-reload
sudo systemctl restart docker

五 镜像操作

1 查看本地仓库有那些镜像

image-20211226161858115
#查看本地仓库有那些镜像
docker imgae ls
或者
docker images

#查看指定镜像名
docker images 镜像名

#只列出镜像id
docker images -q

docker可以通过id和名称:版本来查找镜像

2 下载镜像

docker pull name:tag

3 搜索镜像

查看镜像是否存在,不能查看版本

docker search name

4 删除镜像

#正常删除。只能删除没有运行过的镜像
docker image rm 镜像名(name:tag)|镜像id
#强制删除。镜像和容器一起删除
docker image rm -f 镜像名(name:tag)|镜像id

#举例:删除所有Tomcat的镜像
docker image rm -f $(docker images tomcat -q)

5 镜像备份和恢复

可以将docker打包成一个tar包,方便传输和储存。

#打包镜像
docker save 镜像名:版本 -o (镜像名-版本号).tar

#恢复镜像
docker load -i (tar包的名字).tar

6 查看镜像的构建历史

docker history 镜像名:版本

六 容器的操作

1 查看正在运行的容器

docker ps

#查看所有容器,包括停止的
docker ps -a

#查看所有容器的id
docker ps -aq

2 运行一个容器

docker run name:tag
或者
docker run imageid

container id(容器id)

imgae(基于哪个镜像)

command(容器内启动服务的命令)

names(容器的名称,可以自己指定,不指定则由docker自动分配)

image-20211226164719464

3 容器映射

由于docker是操作系统层面的隔离,所以容器有自己的端口。为了在外部访问到容器,要将容器的端口与外部主机的端口相映射。

docker run -p 8080(宿主机):8080 tomcat:8.0 

-p可以写多个映射

4 后台启动

以守护进程的方式启动。否则每启动一个容器就要重新打开一个

docker run -p 8080(宿主机):8080 -d tomcat:8.0 

5 给容器取名

这个name必须是唯一的

docker run -p 8080(宿主机):8080 -d --name tomcat01 tomcat:8.0

6 停止 重启 启动 暂停 恢复容器

#停止
docker stop 容器名称|容器id
#重启
docker restart 容器名称|容器id
#启动
docker start 容器名称|容器id
#暂停
docker pause 容器名称|容器id
#恢复
docker unpause 容器名称|容器id

7 杀死容器

与stop不同,kill容器容器中不会进行后续操作,属于强制停止。

docker kill 容器名称|容器id

8 删除容器

删除容器不会影响到镜像

#不在运行的容器
docker rm 容器名称|容器id
#强制删除容器
docker rm -f 容器名称|容器id
#删除所有容器
docker rm -f $(docker ps -aq)

9 查看容器日志

docker logs 容器名称|容器id
#查看实时的日志
docker logs -f 容器名称|容器id
#加上宿主机中的时间
docker logs -t 容器名称|容器id

10 进入容器的内部

docker exec -it 容器名称|容器id bash

#-it 代表以交互模式进入
#与容器中的bash交互

#退出容器
exit

11 容器与宿主机之间的文件传输

#将容器中的文件或目录拷贝到宿主机
docker cp 容器名称|容器id:文件或目录 宿主机中的目录

#将宿主机上的文件或目录拷贝到容器中
docker cp 宿主机中的目录或文件 容器名称|容器id:容器的目录

12 查看容器内运行的进程

docker top 容器名称|容器id

13 查看容器内细节指令

docker inspect 容器名称|容器id

14 容器的数据卷机制

data volume:用来实现容器中数据和宿主机中的数据进行映射。通俗来说就是设置一块空间与容器中的目录和宿主机中的目录进行映射。这样容器空间与宿主机的目录就实现了双向绑定。随便更改哪边另一边都会更改。

14.1 数据卷挂载

数据卷只能在容器首次启动时设置

#使用绝对路径设置数据卷(可以多个)
docker run -v 宿主机绝对路径:容器内路径
#注意:这种方式启动时会将容器路径的内容全部清空
docker run -v 宿主机绝对路径:容器内路径:ro
#设置ro时表示容器内的目录是只读的。只能通过宿主机操作。


#使用别名方式设置数据卷
docker run -v 别名:容器内路径
#例如
docker run -v aa:/usr/local/tomcat/webapps
#aa代表docker数据卷中的别名,aa不存在会自动创建,存在则直接使用
#使用别名方式会保留容器路径原始内容,前提是别名对应的路径不存在内容

# 别名挂载还有一种匿名挂载
docker run -d  -v 容器内目录  镜像名/id  

docker根据别名创建的目录一般在/var/lib/docker/volume

#查看所有挂载的卷
docker volume ls

14.2 数据卷容器

一个容器可以通过volumes from直接使用其他容器的数据卷挂载方式。并且这样指定后,多个容器是共享数据卷的,共享的数据卷只要还有容器使用它就不会消失。可以通过这种 机制来实现配合文件的共享或者数据库的数据同步等操作。

docker run -it --name container02 --volumes from container01 镜像名/id  # 将两个容器进行挂载

15 将容器打包成一个镜像

将自己的容器打包成一个镜像,这样以后只需要运行相应的镜像就行了

docker commit -m "描述信息" -a "作者" 容器名称|容器id 镜像名:版本

16 查看容器或者镜像的信息

docker inspect 镜像|容器

七 镜像构成原理

自己打包的镜像为什么那么大?

因为容器是一种轻量级的,可执行的独立软件包,它包含了软件运行的库。

为什么从官方下载的镜像那么小?

因为docker使用了联合文件系统。每个镜像存在一个base镜像,这样就实现了底层镜像的复用,不用存储每个重复的运行依赖。因此,当我们下载Tomcat8.0时,会发现我们要下载许多东西,当我们下载Tomcat8.1时会发现下载的东西变少了。

每个镜像都由很多层次构成,Docker 使用 Union FS (opens new window)将这些不同的层结合到一个镜像中去。

通常 Union FS 有两个用途, 一方面可以实现不借助 LVM、RAID 将多个 disk 挂到同一个目录下,另一个更常用的就是将一个只读的分支和一个可写的分支联合在一起,Live CD 正是基于此方法可以允许在镜像不变的基础上允许用户在其上进行一些写操作。

Docker 在 OverlayFS 上构建的容器也是利用了类似的原理。

八 dockerFile

1 概念

dockerfile是用来构建docker镜像的文件,是一个命令参数脚本

官方镜像的很多都是基础包,我们通常会构建自己的镜像。

构建步骤:

  1. 编写一个dockerfile文件
  2. docker build为一个镜像
  3. docker run运行镜像
  4. docker push发布镜像

2 指令

命令 效果
FROM 基础镜像:Centos/Ubuntu
MAINTAINER 镜像作者+邮箱
RUN 镜像构建的时候需要运行的命令
ADD 为镜像添加内容(压缩包)
WORKDIR 镜像工作目录(进入容器时的目录)
VOLUME 挂载的目录
EXPOSE 暴露端口配置
CMD/ENTRYPOINT 指定这个容器启动时要运行的命令(CMD替代先前命令,ENTRYPOINT在先前命令后追加)
COPY 类似于ADD,将文件拷贝到镜像中
ENV 构建时设置环境变量

3 构建自己的CentOS

下载CentOS基础镜像

docker pull centos:latest

创建dockerfile文件

vim mydockerfile-centos

写dockerfile文件

#选择基础镜像。centos8 21年的时候停止了源服务,因此yum命令会报错。这里直接指定7版本
FROM centos:7
#作者信息
MAINTAINER qjk<1256929691@qq.com>

#环境变量
ENV MYPATH /usr/local
#进入容器时的目录
WORKDIR $MYPATH

#需要运行的命令
RUN yum -y install vim
RUN yum -y install net-tools

#暴露端口
EXPOSE 8888

#输出一些信息
CMD echo $MYPATH
CMD echo "---end---"
#运行bash
CMD /bin/bash

构建镜像

#-f 使用的dockerfile文件 -t 镜像名:版本号 . 当前上下文环境
docker build -f mydockerfile-centos -t mycentos:1.0 .

启动

docker run -it mycentos:1.0

测试发现安装成功

[root@d92186b194dc local]# ifconfig 
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 172.17.0.2  netmask 255.255.0.0  broadcast 172.17.255.255
        ether 02:42:ac:11:00:02  txqueuelen 0  (Ethernet)
        RX packets 8  bytes 656 (656.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 0  bytes 0 (0.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
        inet 127.0.0.1  netmask 255.0.0.0
        loop  txqueuelen 1000  (Local Loopback)
        RX packets 0  bytes 0 (0.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 0  bytes 0 (0.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0
[root@localhost data]# docker history mycentos:1.0
IMAGE          CREATED          CREATED BY                                      SIZE      COMMENT
68ec40605859   21 minutes ago   /bin/sh -c #(nop)  CMD ["/bin/sh" "-c" "/bin…   0B        
a611dbac6ce6   21 minutes ago   /bin/sh -c #(nop)  CMD ["/bin/sh" "-c" "echo…   0B        
3760049dc190   21 minutes ago   /bin/sh -c #(nop)  CMD ["/bin/sh" "-c" "echo…   0B        
8a9d3aecc1fb   21 minutes ago   /bin/sh -c #(nop)  EXPOSE 8888                  0B        
ba8c71fa6dde   21 minutes ago   /bin/sh -c yum -y install net-tools             171MB     
1de039243935   21 minutes ago   /bin/sh -c yum -y install vim                   226MB     
7f9dde17bf22   21 minutes ago   /bin/sh -c #(nop) WORKDIR /usr/local            0B        
97ada81ddf08   21 minutes ago   /bin/sh -c #(nop)  ENV MYPATH=/usr/local        0B        
53e586d5ba0a   22 minutes ago   /bin/sh -c #(nop)  MAINTAINER qjk<1256929691…   0B        
eeb6ee3f44bd   9 months ago     /bin/sh -c #(nop)  CMD ["/bin/bash"]            0B        
<missing>      9 months ago     /bin/sh -c #(nop)  LABEL org.label-schema.sc…   0B        
<missing>      9 months ago     /bin/sh -c #(nop) ADD file:b3ebbe8bd304723d4…   204MB     

4 CMD和ENTRYPOINT的区别

例如dockerfile中使用CMD命令

CMD ["ls","-a"]

后续启动容器时想要执行ls -l命令,这样写会报错

docker run -it mycentos:1.0 -l

因为-l会直接替换ls -a,正确的写法是这样

docker run -it mycentos:1.0 ls -l

例如dockerfile中使用ENTRYPOINT命令

ENTRYPOINT ["ls","-a"]

这样写就不会报错

docker run -it mycentos:1.0 -l

5 构建Tomcat

Dockerfile文件

FROM centos
  
COPY readme.md /usr/local/readme.md

#添加压缩包,docker会自动解压
ADD jdk8.tar.gz /usr/local
ADD tomcat.tar.gz /usr/local

RUN yum -y install vim

ENV MYPATH /usr/local
WORKDIR $MYPATH

ENV JAVA_HOME /usr/local/jdk1.8.0_141
ENV CLASSPATH $JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar
ENV PATH $PATH:$JAVA_HOME/bin

EXPOSE 8080

#命令可以用&&直接拼接
CMD /usr/local/apache-tomcat-10.0.12/bin/startup.sh && tail -F /usr/local/apache-tomcat-10.0.12/bin/logs/catalina.out

6 发布镜像

dockerhub

  1. 注册dockerhub账号
  2. 登录dockerhub
  3. 发布

登录:

docker login -u 用户名

通过tag创建版本号

docker tag 镜像名|id 新的镜像名:版本号

发布

docker push 镜像名:版本号

九 Docker网络

1 Docker0

通过命令ip addr查看本地ip地址,我们发现除了本机回环地址和埃里远的内网地址外,还多了一个网卡:Docker0,这是Docker服务启动后自动生成的。

而如果进入一个正在后台运行的tomcat容器,同样使用ip addr命令,发现容器得到了一个新的网络:12: eth@if13,ip地址:172.17.0.2。这是Docker在容器启动时为其分配的。

docker容器的网络是通过veth(Linux提供的虚拟网卡接口)来管理的。宿主机和容器分别连接到一个虚拟网卡接口。例如:宿主机一个新网络13: vethda1df4b@if12,对应容器内网络地址的12: eth@if13。12和13分别是宿主机端和容器端的接口编号。宿主机和容器之间可以ping通,容器与容器间也可以ping通,会通过docker0转发,docker0类似于局域网中的路由器。

image-20220624095350600

容器一删除,对应的网桥也会随之删除。

若编写一个微服务并连接数据库,如果数据库ip改变,如何根据容器名而不是ip访问容器?显然,直接使用容器名是无法ping通容器内部的:

这时我们可以在容器启动命令中加入一个选项:–link,使得我们可以根据容器名来访问容器。

docker run -d -P --link 容器名/id 镜像名/id

#例如:启动一个容器并与tomcat01进行相连
docker run -d -P --link tomcat01 --name tomcat02 tomcat:8 

连接后,tomcat02可以直接ping tomcat01,而tomcat01却不能ping通tomcat02

实现原理:在tomcat02的host文件中添加了tomcat01的ip地址。

显然这种方式过于低级,无法解决上述问题,因此--link不建议使用。

在实际开发中,docker0网卡的局限性过大,不支持服务名访问,因此往往会使用自己的网卡代替docker0。

3 自定义网络

# 查看所有的docker网络
[root@localhost ~]# docker network ls
NETWORK ID     NAME      DRIVER    SCOPE
8d9e97cd0f61   bridge    bridge    local
7d43eef7b13e   host      host      local
bc1e545c960e   none      null      local

docker中的网络模式有:

  • bridge:桥接(docker默认)/
  • none:不配置网络 /
  • host:和宿主机共享网络

docker run命令默认带有一个参数–net bridge,此处的bridge指的就是docker0。可以自己手动创建一个新的网络。

docker  network create --driver 网络模式 --subnet 子网ip --gateway 网关 网络名     

#例如
docker  network create --driver bridge --subnet 192.168.1.0/16 --gateway 192.168.1.1 mynet     

创建容器并使用自己创建的网络。这样创建的容器可以通过ping容器名直接ping通。

docker run -d --name tomcat01 -net mynet tomcat:8

4 网络连通

对于建立在不同网络下(docker0, newnet)的两个容器tomcat01和tomcat02,他们的网段不同,因此是无法彼此ping通容器内部的:

这时我们需要通过docker network connect命令打通容器与网络之间的连接:

docker network connect 网络名 容器名/id

例如:
docker network connect mynet tomcat01

十 springboot微服务打包成镜像

打包成jar包

mvn package

编写Dockerfile

FROM java:8
COPY *.jar /app.jar
CMD ["--server.port=8080"]
EXPOSE 8080
ENTRYPOINT ["java","-jar","app.jar"]

构建镜像

# 1.复制jar和DockerFIle到服务器
# 2.构建镜像
docker build -t xxxxx:xx  .

文章作者: meditate
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 meditate !
  目录