Docker 是一个开源的应用容器引擎,基于 Go 语言 并遵从 Apache2.0 协议开源。

Docker 可以让开发者打包他们的应用以及依赖包到一个轻量级、可移植的容器中,然后发布到任何流行的 Linux 机器上,也可以实现虚拟化。

容器是完全使用沙箱机制,相互之间不会有任何接口(类似 iPhone 的 app),更重要的是容器性能开销极低。

Docker 从 17.03 版本之后分为 CE(Community Edition: 社区版) 和 EE(Enterprise Edition: 企业版),我们用社区版就可以了。

应用场景

  • Web 应用的自动化打包和发布。

  • 自动化测试和持续集成、发布。

  • 在服务型环境中部署和调整数据库或其他的后台应用。

  • 从头编译或者扩展现有的 OpenShift 或 Cloud Foundry 平台来搭建自己的 PaaS 环境。

优点

1、快速,一致地交付您的应用程序
Docker 允许开发人员使用您提供的应用程序或服务的本地容器在标准化环境中工作,从而简化了开发的生命周期。

容器非常适合持续集成和持续交付(CI / CD)工作流程,请考虑以下示例方案:

您的开发人员在本地编写代码,并使用 Docker 容器与同事共享他们的工作。
他们使用 Docker 将其应用程序推送到测试环境中,并执行自动或手动测试。
当开发人员发现错误时,他们可以在开发环境中对其进行修复,然后将其重新部署到测试环境中,以进行测试和验证。
测试完成后,将修补程序推送给生产环境,就像将更新的镜像推送到生产环境一样简单。
2、响应式部署和扩展
Docker 是基于容器的平台,允许高度可移植的工作负载。Docker 容器可以在开发人员的本机上,数据中心的物理或虚拟机上,云服务上或混合环境中运行。

Docker 的可移植性和轻量级的特性,还可以使您轻松地完成动态管理的工作负担,并根据业务需求指示,实时扩展或拆除应用程序和服务。

3、在同一硬件上运行更多工作负载
Docker 轻巧快速。它为基于虚拟机管理程序的虚拟机提供了可行、经济、高效的替代方案,因此您可以利用更多的计算能力来实现业务目标。Docker 非常适合于高密度环境以及中小型部署,而您可以用更少的资源做更多的事情。

docker架构

Docker 包括三个基本概念:

  • 镜像(Image):Docker 镜像(Image),就相当于是一个 root 文件系统。比如官方镜像 ubuntu:16.04 就包含了完整的一套 Ubuntu16.04 最小系统的 root 文件系统。
  • 容器(Container):镜像(Image)和容器(Container)的关系,就像是面向对象程序设计中的类和实例一样,镜像是静态的定义,容器是镜像运行时的实体。容器可以被创建、启动、停止、删除、暂停等。
  • 仓库(Repository):仓库可看成一个代码控制中心,用来保存镜像。

Docker 使用客户端-服务器 (C/S) 架构模式,使用远程API来管理和创建Docker容器。

Docker 容器通过 Docker 镜像来创建。

容器与镜像的关系类似于面向对象编程中的对象与类。

概念 说明
Docker 镜像(Images) Docker 镜像是用于创建 Docker 容器的模板,比如 Ubuntu 系统。
Docker 容器(Container) 容器是独立运行的一个或一组应用,是镜像运行时的实体。
Docker 客户端(Client) Docker 客户端通过命令行或者其他工具使用 Docker SDK (https://docs.docker.com/develop/sdk/) 与 Docker 的守护进程通信。
Docker 主机(Host) 一个物理或者虚拟的机器用于执行 Docker 守护进程和容器。
Docker Registry Docker 仓库用来保存镜像,可以理解为代码控制中的代码仓库。Docker Hub(https://hub.docker.com) 提供了庞大的镜像集合供使用。一个 Docker Registry 中可以包含多个仓库(Repository);每个仓库可以包含多个标签(Tag);每个标签对应一个镜像。通常,一个仓库会包含同一个软件不同版本的镜像,而标签就常用于对应该软件的各个版本。我们可以通过 <仓库名>:<标签> 的格式来指定具体是这个软件哪个版本的镜像。如果不给出标签,将以 latest 作为默认标签。
Docker Machine Docker Machine是一个简化Docker安装的命令行工具,通过一个简单的命令行即可在相应的平台上安装Docker,比如VirtualBox、 Digital Ocean、Microsoft Azure。

docker安装

docker官方资料http://www.dockerinfo.net/document

更换rockylinux8的国内镜像源

sed -e 's|^mirrorlist=|#mirrorlist=|g' \-e 's|^#baseurl=http://dl.rockylinux.org/$contentdir|baseurl=https://mirrors.aliyun.com/rockylinux|g' \-i.bak \/etc/yum.repos.d/Rocky-*.repoyum makecacheyum install -y yum-utils device-mapper-persistent-data lvm2yum-config-manager --add-repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repoyum -y install docker-cesystemctl start docker
systemctl enable docker
docker version

了解:卸载docker

yum remove docker-ce docker-ce-cli containerd.io
rm -rf /var/docker #docker的默认工作路径

配置镜像加速

sudo mkdir -p /etc/docker
sudo tee /etc/docker/daemon.json <<-'EOF'
{"registry-mirrors": ["https://5yq2y3im.mirror.aliyuncs.com"]
}
EOF
systemctl daemon-reload
systemctl restart docker

底层原理

docker为什么比VM快?
1、docker有着比虚拟机更少的抽象层

2、docker利用的是宿主机的内核,VM需要是Guest OS,

所以说新建一个容器的时候,docker不需要像虚拟机一样重新加载一个操作系统内核,避免引导,虚拟机是加载Guest OS,分钟级别的,而docker是利用宿主机的操作系统, 跳过了这个复杂过程,秒级。

docker常用命令

帮助命令

docker官方文档:https://docs.docker.com/engine/reference/commandline/docker/

docker version #版本信息docker info  #docker的系统信息,包括镜像和容器docker --help

镜像命令

docker images 查看所有本地主机上的镜像

[root@docker ~]# docker images
REPOSITORY    TAG       IMAGE ID       CREATED         SIZE
hello-world   latest    9c7a54a9a43c   4 weeks ago     13.3kB
mysql         latest    3218b38490ce   17 months ago   516MBREPOSITORY #镜像仓库源
TAG        #镜像标签
IMAGE ID    #镜像ID
CREATED     #创建时间
SIZE        #大小
#可选项-a, --all      #列出所有镜像-q, --quiet   #只显示镜像的ID

docker pull 下载镜像

[root@docker ~]# 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 删除镜像

docker rmi 镜像ID #删除指定的镜像
docker rmi $(docker images -qa) #删除全部的镜像

容器命令

新建容器并启动

docker run [可选参数] image
#参数说明
-d, --detach=false, 指定容器运行于前台还是后台,默认为false
-i, --interactive=false, 打开STDIN,用于控制台交互
-t, --tty=false, 分配tty设备,该可以支持终端登录,默认为false
-u, --user="", 指定容器的用户
-a, --attach=[], 登录容器(必须是以docker run -d启动的容器)
-w, --workdir="", 指定容器的工作目录
-c, --cpu-shares=0, 设置容器CPU权重,在CPU共享场景使用
-e, --env=[], 指定环境变量,容器中可以使用该环境变量
-m, --memory="", 指定容器的内存上限
-P, --publish-all=false, 指定容器暴露的端口
-p, --publish=[], 指定容器暴露的端口
-h, --hostname="", 指定容器的主机名
-v, --volume=[], 给容器挂载存储卷,挂载到容器的某个目录
--volumes-from=[], 给容器挂载其他容器上的卷,挂载到容器的某个目录
--cap-add=[], 添加权限,权限清单详见:http://linux.die.net/man/7/capabilities
--cap-drop=[], 删除权限,权限清单详见:http://linux.die.net/man/7/capabilities
--cidfile="", 运行容器后,在指定文件中写入容器PID值,一种典型的监控系统用法
--cpuset="", 设置容器可以使用哪些CPU,此参数可以用来容器独占CPU
--device=[], 添加主机设备给容器,相当于设备直通
--dns=[], 指定容器的dns服务器
--dns-search=[], 指定容器的dns搜索域名,写入到容器的/etc/resolv.conf文件
--entrypoint="", 覆盖image的入口点
--env-file=[], 指定环境变量文件,文件格式为每行一个环境变量
--expose=[], 指定容器暴露的端口,即修改镜像的暴露端口
--link=[], 指定容器间的关联,使用其他容器的IP、env等信息
--lxc-conf=[], 指定容器的配置文件,只有在指定--exec-driver=lxc时使用
--name="", 指定容器名字,后续可以通过名字进行容器管理,links特性需要使用名字
--net="bridge", 容器网络设置:
bridge 使用docker daemon指定的网桥
host //容器使用主机的网络
container:NAME_or_ID >//使用其他容器的网路,共享IP和PORT等网络资源
none 容器使用自己的网络(类似--net=bridge),但是不进行配置
--privileged=false, 指定容器是否为特权容器,特权容器拥有所有的capabilities
--restart="no", 指定容器停止后的重启策略:
no:容器退出时不重启
on-failure:容器故障退出(返回值非零)时重启
always:容器退出时总是重启
docker ps 查看容器docker  rm -f $(docker ps -aq) #删除所有容器docker start 容器id #启动
docker restart 容器ID #重启
docker stop 容器ID #停止
docker kill 容器ID  #强制删除查看日志
docker logs [OPTIONS] CONTAINER
docker logs --help
-- tf #显示日志
--tail number #显示日志条数
[root@docker ~]# docker logs -tf --tail 10 57766036a15a
2023-06-05T01:39:34.157079141Z qhz
2023-06-05T01:39:35.159944994Z qhz
2023-06-05T01:39:36.163022053Z qhz
2023-06-05T01:39:37.165957938Z qhz
2023-06-05T01:39:38.169695142Z qhz
2023-06-05T01:39:39.174819925Z qhz
2023-06-05T01:39:40.179299989Z qhz
2023-06-05T01:39:41.184506664Z qhz

查看容器中进程信息ps

docker top 容器ID
^C
[root@docker ~]# docker top 57766036a15a
UID                 PID                 PPID                C                   STIME               TTY                 TIME                CMD
root                2119                2099                0                   09:36               ?                   00:00:00            /bin/sh -c while true;do echo qhz;sleep 1;done
root                2548                2119                0                   09:42               ?                   00:00:00            /usr/bin/coreutils --coreutils-prog-shebang=sleep /usr/bin/sleep 1

查看镜像元数据

docker inspect 容器ID
[root@docker ~]# docker inspect 57766036a15a
[{"Id": "57766036a15a56b63dcbc0c4187062dad39b15d93c2e9726fef9d9c7a7b2f4b3","Created": "2023-06-05T01:36:09.626896374Z","Path": "/bin/sh","Args": ["-c","while true;do echo qhz;sleep 1;done"],"State": {"Status": "running","Running": true,"Paused": false,"Restarting": false,"OOMKilled": false,"Dead": false,"Pid": 2119,"ExitCode": 0,"Error": "","StartedAt": "2023-06-05T01:36:10.126955207Z","FinishedAt": "0001-01-01T00:00:00Z"},"Image": "sha256:5d0da3dc976460b72c77d94c8a1ad043720b0416bfc16c52c45d4847e53fadb6","ResolvConfPath": "/var/lib/docker/containers/57766036a15a56b63dcbc0c4187062dad39b15d93c2e9726fef9d9c7a7b2f4b3/resolv.conf","HostnamePath": "/var/lib/docker/containers/57766036a15a56b63dcbc0c4187062dad39b15d93c2e9726fef9d9c7a7b2f4b3/hostname","HostsPath": "/var/lib/docker/containers/57766036a15a56b63dcbc0c4187062dad39b15d93c2e9726fef9d9c7a7b2f4b3/hosts","LogPath": "/var/lib/docker/containers/57766036a15a56b63dcbc0c4187062dad39b15d93c2e9726fef9d9c7a7b2f4b3/57766036a15a56b63dcbc0c4187062dad39b15d93c2e9726fef9d9c7a7b2f4b3-json.log","Name": "/centos","RestartCount": 0,"Driver": "overlay2","Platform": "linux","MountLabel": "","ProcessLabel": "","AppArmorProfile": "","ExecIDs": null,"HostConfig": {"Binds": null,"ContainerIDFile": "","LogConfig": {"Type": "json-file","Config": {}},"NetworkMode": "default","PortBindings": {},"RestartPolicy": {"Name": "no","MaximumRetryCount": 0},"AutoRemove": false,"VolumeDriver": "","VolumesFrom": null,"ConsoleSize": [24,84],"CapAdd": null,"CapDrop": null,"CgroupnsMode": "host","Dns": [],"DnsOptions": [],"DnsSearch": [],"ExtraHosts": null,"GroupAdd": null,"IpcMode": "private","Cgroup": "","Links": null,"OomScoreAdj": 0,"PidMode": "","Privileged": false,"PublishAllPorts": false,"ReadonlyRootfs": false,"SecurityOpt": null,"UTSMode": "","UsernsMode": "","ShmSize": 67108864,"Runtime": "runc","Isolation": "","CpuShares": 0,"Memory": 0,"NanoCpus": 0,"CgroupParent": "","BlkioWeight": 0,"BlkioWeightDevice": [],"BlkioDeviceReadBps": [],"BlkioDeviceWriteBps": [],"BlkioDeviceReadIOps": [],"BlkioDeviceWriteIOps": [],"CpuPeriod": 0,"CpuQuota": 0,"CpuRealtimePeriod": 0,"CpuRealtimeRuntime": 0,"CpusetCpus": "","CpusetMems": "","Devices": [],"DeviceCgroupRules": null,"DeviceRequests": null,"MemoryReservation": 0,"MemorySwap": 0,"MemorySwappiness": null,"OomKillDisable": false,"PidsLimit": null,"Ulimits": null,"CpuCount": 0,"CpuPercent": 0,"IOMaximumIOps": 0,"IOMaximumBandwidth": 0,"MaskedPaths": ["/proc/asound","/proc/acpi","/proc/kcore","/proc/keys","/proc/latency_stats","/proc/timer_list","/proc/timer_stats","/proc/sched_debug","/proc/scsi","/sys/firmware"],"ReadonlyPaths": ["/proc/bus","/proc/fs","/proc/irq","/proc/sys","/proc/sysrq-trigger"]},"GraphDriver": {"Data": {"LowerDir": "/var/lib/docker/overlay2/cf98ae2892b5278668f845001a0b8670d38eeb5026b9fbf7aec1aa40435dc372-init/diff:/var/lib/docker/overlay2/6eb4b884bcd4b4e94a2aa731c20fc18c87d6c03aced64b7707b2a96235ad0e9f/diff","MergedDir": "/var/lib/docker/overlay2/cf98ae2892b5278668f845001a0b8670d38eeb5026b9fbf7aec1aa40435dc372/merged","UpperDir": "/var/lib/docker/overlay2/cf98ae2892b5278668f845001a0b8670d38eeb5026b9fbf7aec1aa40435dc372/diff","WorkDir": "/var/lib/docker/overlay2/cf98ae2892b5278668f845001a0b8670d38eeb5026b9fbf7aec1aa40435dc372/work"},"Name": "overlay2"},"Mounts": [],"Config": {"Hostname": "57766036a15a","Domainname": "","User": "","AttachStdin": false,"AttachStdout": false,"AttachStderr": false,"Tty": false,"OpenStdin": false,"StdinOnce": false,"Env": ["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"],"Cmd": ["/bin/sh","-c","while true;do echo qhz;sleep 1;done"],"Image": "centos:latest","Volumes": null,"WorkingDir": "","Entrypoint": null,"OnBuild": null,"Labels": {"org.label-schema.build-date": "20210915","org.label-schema.license": "GPLv2","org.label-schema.name": "CentOS Base Image","org.label-schema.schema-version": "1.0","org.label-schema.vendor": "CentOS"}},"NetworkSettings": {"Bridge": "","SandboxID": "62f0d39d744ec7ce163f311427c5a0f1d3856287eeb7d4310e075b3bfa6a5b78","HairpinMode": false,"LinkLocalIPv6Address": "","LinkLocalIPv6PrefixLen": 0,"Ports": {},"SandboxKey": "/var/run/docker/netns/62f0d39d744e","SecondaryIPAddresses": null,"SecondaryIPv6Addresses": null,"EndpointID": "300cd62f0d5dd7b89359367cda3b0fd72e16fcc6ff292ad45ca23b0ccbae13e9","Gateway": "172.17.0.1","GlobalIPv6Address": "","GlobalIPv6PrefixLen": 0,"IPAddress": "172.17.0.2","IPPrefixLen": 16,"IPv6Gateway": "","MacAddress": "02:42:ac:11:00:02","Networks": {"bridge": {"IPAMConfig": null,"Links": null,"Aliases": null,"NetworkID": "4670e51daae0e26f088a8d98d3ece5d24fbf60eb1298619ff2aa210373a6e747","EndpointID": "300cd62f0d5dd7b89359367cda3b0fd72e16fcc6ff292ad45ca23b0ccbae13e9","Gateway": "172.17.0.1","IPAddress": "172.17.0.2","IPPrefixLen": 16,"IPv6Gateway": "","GlobalIPv6Address": "","GlobalIPv6PrefixLen": 0,"MacAddress": "02:42:ac:11:00:02","DriverOpts": null}}}}
]

进入容器命令

docker exec -it 容器ID /bin/bash #进入容器后开启一个新的终端
docker attach 容器ID #进入容器正在执行的终端

从容器内拷贝文件到主机上

docker cp 容器ID:容器内路径 目的主机的路径
#拷贝是一个手动过程,未来我们使用-v逻辑卷,实现自动同步

docker安装程序

docker中安装Nginx

[root@docker ~]# docker pull nginx
[root@docker ~]# docker images
[root@docker ~]# docker run -d --name nginx1 -p 3344:80 nginx
c92cc2fc9dfa13aba0ef293934298454dff1e9ecdaa42af18e90eede17d050fb
[root@docker ~]# docker ps
CONTAINER ID   IMAGE           COMMAND                   CREATED          STATUS          PORTS                                   NAMES
c92cc2fc9dfa   nginx           "/docker-entrypoint.…"   10 seconds ago   Up 7 seconds    0.0.0.0:3344->80/tcp, :::3344->80/tcp   nginx1
[root@docker ~]# 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>
端口暴露概念

docker安装 tomcat

[root@docker ~]# docker pull tomcat
[root@docker ~]# docker run -d --name tomcat1 -p 3355:8080 tomcat:latest
804560e03f7263d6d1103d67d900cd6c08bf0b9ba404c89d50b4218e1e453848
[root@docker ~]# docker ps
CONTAINER ID   IMAGE           COMMAND                   CREATED             STATUS             PORTS                                       NAMES
804560e03f72   tomcat:latest   "catalina.sh run"         25 seconds ago      Up 23 seconds      0.0.0.0:3355->8080/tcp, :::3355->8080/tcp   tomcat1
#进入容器
[root@docker ~]# docker exec -it 804560e03f72 /bin/bash
#发现问题:1、linux命令少了,没有webapps,阿里云默认最小的镜像,已经将没必要的都去掉了,保证最小可运行的环境。

docker部署ES+kibana

[root@docker ~]# docker pull elasticsearch
[root@docker ~]# docker run -d --name elasticsearch -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" elasticsearch:latest
1ae3e373b40b78b61e5e85add46c42dd535a2a5594cfba9697ffa79f19db4fe9
Elasticsearch目录结构如下:
bin :脚本文件,包括 ES 启动 & 安装插件等等
config : elasticsearch.yml(ES 配置文件)、jvm.options(JVM 配置文件)、日志配置文件等等
lib : 类库
logs : 日志文件
modules : ES 所有模块,包括 X-pack 等
plugins : ES 已经安装的插件。默认没有插件
data : ES 启动的时候,会有该目录,用来存储文档数据。该目录可以设置[root@docker ~]# curl localhost:9200
{"name" : "nGMmHjy","cluster_name" : "elasticsearch","cluster_uuid" : "JqDooVpbT9GUw1G8SOag5A","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"
}
#进入容器修改配置,解决跨域访问问题
docker exec -it elasticsearch /bin/bash
root@ac9cad2ef2f2:/usr/share/elasticsearch/config# vim elasticsearch.yml
#添加在末尾
http.cors.enabled: true
http.cors.allow-origin: "*"
[root@docker ~]# docker restart elasticsearch
[root@docker ~]# docker inspect elasticsearch #查看对应的IP地址
[root@docker ~]# docker pull kibana
[root@docker ~]# docker run -d --name kibana -e ELASTICSEARCH_HOSTS=http://172.18.0.2:9200 -p 5601:5601 -d kibana:latest
root@e1af140ea882:/etc/kibana# vim kibana.yml
docker restart kibana

可视化

portainer(先用这个)

docker图形化界面管理工具,提供一个后台面板供我们操作。

docker run -d -p 8088:9000 --restart=always -v /var/run/docker.sock:/var/run/docker.sock --privileged=true portainer/portainer

访问测试:http://本地IP:8088



Rancher(CI/CD再用)

docker镜像讲解

docker镜像加载原理

UnionFS(联合文件系统)

联合文件系统( UnionFS)是一种分层、轻量级并且高性能的文件系统,它支持对文件系统的修改作为一次提交来一层层的叠加,同时可以将不同目录挂载到同一个虚拟文件系统下(unite several directories into a single virtual filesystem)。

联合文件系统是 Docker 镜像的基础。镜像可以通过分层来进行继承,基于基础镜像(没有父镜像),可以制作各种具体的应用镜像。另外,不同 Docker 容器就可以共享一些基础的文件系统层,同时再加上自己独有的改动层,大大提高了存储的效率。

特性:一次同时加载多个文件系统,但从外面看起来,只能看到一个文件系统,联合加载会把各层文件系统叠加起来,这样最终文件系统会包括所有底层的文件和目录

Docker 目前支持的联合文件系统种类包括 AUFS, btrfs, vfs 和 DeviceMapper。

docker镜像加载原理

docker的镜像实际上由一层一层的文件系统组成,这种层级的文件系统UnionFS。

bootfs(boot file system)主要包含bootloader和kernel,bootloader主要是引导加载kernel,linux刚启动时会加载 bootfs文件系统,在docker镜像的最底层是bootfs,这一层与我们典型的linux/unix系统是一样的,包含boot加载器和内核,当boot加载完成之后整个内核就都在内存中,此时内存的使用权已由bootfs转交给内核,此时系统也会卸载bootfs。

rootfs(root file system) 在bootfs之上,包含的就是典型的linux系统中的/dev,/porc,/etc/等标准目录,rootfs就是各种不同的操作系统发行版,比如centos,Ubuntu等等。

分层理解

docker镜像采用这种分层结构,好处莫过于资源共享,比如有多个镜像都从相同的Base镜像构建而来,那么宿主机只需要在磁盘上保留一份Base镜像,同时内存中也只需要加载一份Base镜像,这样就可以为所有的容器服务,而且镜像的没一层都可以被共享。

查看镜像分层的方式可以通过docker image inspect命令。

[root@docker ~]# docker image inspect redis
[{"Id": "sha256:7614ae9453d1d87e740a2056257a6de7135c84037c367e1fffa92ae922784631","RepoTags": ["redis:latest"],"RepoDigests": ["redis@sha256:db485f2e245b5b3329fdc7eff4eb00f913e09d8feb9ca720788059fdc2ed8339"],"Parent": "","Comment": "","Created": "2021-12-21T12:42:49.755107412Z","Container": "13d25f53410417c5220c8dfe8bd49f06abdbcd69faa62a9b877de02464bb04a3","ContainerConfig": {"Hostname": "13d25f534104","Domainname": "","User": "","AttachStdin": false,"AttachStdout": false,"AttachStderr": false,"ExposedPorts": {"6379/tcp": {}},"Tty": false,"OpenStdin": false,"StdinOnce": false,"Env": ["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin","GOSU_VERSION=1.12","REDIS_VERSION=6.2.6","REDIS_DOWNLOAD_URL=http://download.redis.io/releases/redis-6.2.6.tar.gz","REDIS_DOWNLOAD_SHA=5b2b8b7a50111ef395bf1c1d5be11e6e167ac018125055daa8b5c2317ae131ab"],"Cmd": ["/bin/sh","-c","#(nop) ","CMD [\"redis-server\"]"],"Image": "sha256:e093f59d716c95cfce82c676f099b960cc700432ab531388fcedf79932fc81ec","Volumes": {"/data": {}},"WorkingDir": "/data","Entrypoint": ["docker-entrypoint.sh"],"OnBuild": null,"Labels": {}},"DockerVersion": "20.10.7","Author": "","Config": {"Hostname": "","Domainname": "","User": "","AttachStdin": false,"AttachStdout": false,"AttachStderr": false,"ExposedPorts": {"6379/tcp": {}},"Tty": false,"OpenStdin": false,"StdinOnce": false,"Env": ["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin","GOSU_VERSION=1.12","REDIS_VERSION=6.2.6","REDIS_DOWNLOAD_URL=http://download.redis.io/releases/redis-6.2.6.tar.gz","REDIS_DOWNLOAD_SHA=5b2b8b7a50111ef395bf1c1d5be11e6e167ac018125055daa8b5c2317ae131ab"],"Cmd": ["redis-server"],"Image": "sha256:e093f59d716c95cfce82c676f099b960cc700432ab531388fcedf79932fc81ec","Volumes": {"/data": {}},"WorkingDir": "/data","Entrypoint": ["docker-entrypoint.sh"],"OnBuild": null,"Labels": null},"Architecture": "amd64","Os": "linux","Size": 112691373,"VirtualSize": 112691373,"GraphDriver": {"Data": {"LowerDir": "/var/lib/docker/overlay2/8ab6d8bb3e857055d3923e635bd64a0e799245c02e725074a36ff10402fc81b8/diff:/var/lib/docker/overlay2/53a7d9746e1ec40cd89583afcaca59d8346ceb3a8f42d0a5888a4814ddd33259/diff:/var/lib/docker/overlay2/06cbc301f21651802cde49c292a05119268053dbc58b328971ccbe7842f05462/diff:/var/lib/docker/overlay2/481ac78311e0a3d9f1dc8ee0d42b2ff3df1c0313b5f3ac6532c55d6f58ee0014/diff:/var/lib/docker/overlay2/6f824c4190ec5cb695032064953e090e2bc42f9803d59ecd939fbc6e22210a56/diff","MergedDir": "/var/lib/docker/overlay2/3d2c6f23de56940a5e9ab96117baebcda2aab6e6f6cdf1d79f63d7f667ca8664/merged","UpperDir": "/var/lib/docker/overlay2/3d2c6f23de56940a5e9ab96117baebcda2aab6e6f6cdf1d79f63d7f667ca8664/diff","WorkDir": "/var/lib/docker/overlay2/3d2c6f23de56940a5e9ab96117baebcda2aab6e6f6cdf1d79f63d7f667ca8664/work"},"Name": "overlay2"},"RootFS": {"Type": "layers","Layers": ["sha256:2edcec3590a4ec7f40cf0743c15d78fb39d8326bc029073b41ef9727da6c851f","sha256:9b24afeb7c2f21e50a686ead025823cd2c6e9730c013ca77ad5f115c079b57cb","sha256:4b8e2801e0f956a4220c32e2c8b0a590e6f9bd2420ec65453685246b82766ea1","sha256:529cdb636f61e95ab91a62a51526a84fd7314d6aab0d414040796150b4522372","sha256:9975392591f2777d6bf4d9919ad1b2c9afa12f9a9b4d260f45025ec3cc9b18ed","sha256:8e5669d8329116b8444b9bbb1663dda568ede12d3dbcce950199b582f6e94952"]},"Metadata": {"LastTagTime": "0001-01-01T00:00:00Z"}}
]

理解:

所有的docker镜像都起始于一个基础镜像层,当进行或增加新的内容时,就会在当前镜像之上,创建一个新的镜像层。举一个简单的例子,基于Ubuntu Linux16.4创建一个新的镜像,这就是新镜像的第一层,如果在该镜像中添加python包,就会在基础镜像层之上创建第二个镜像层,如果继续添加一个安全补丁,就会创建第三个镜像层,该镜像当前已经包含3个镜像层,如下图所示。

特点

docker镜像都是只读,当容器启动时,一个新的可写层被加载到镜像的顶部,这一层就是我们的容器层,容器之下都叫镜像层。

提交一个自己的镜像

commit镜像

docker commit 提交容器成为一个新的副本
docker commit -m=“提交的描述信息” -a="作者" 容器ID 目标镜像名:tag

实战测试

#1、启动一个默认的tomcat
#2、发现这个默认的Tomcat是没有的webapps应用,镜像的原因,官方的镜像默认webapps下是没有目录的,
#3、我自己拷贝进去基本的文件
#4、将我们操作过的容器通过commit提交成一个镜像,

容器数据卷

Docker将运用与运行的环境打包形成容器运行, Docker容器产生的数据,如果不通过docker commit生成新的镜像,使得数据做为镜像的一部分保存下来, 那么当容器删除后,数据自然也就没有了。 为了能保存数据在Docker中我们使用卷。|

卷就是目录或文件,存在于一个或多个容器中,由Docker挂载到容器,但卷不属于联合文件系统(Union FileSystem),因此能够绕过联合文件系统提供一些用于持续存储或共享数据的特性:。

卷的设计目的就是数据的持久化,完全独立于容器的生存周期,因此Docker不会在容器删除时删除其挂载的数据卷。

Docker容器卷的工作就是将docker容器数据通过映射进行备份+持久化到本地的主机目录

使用数据卷

方式一:直接使用命令挂载

[root@docker ~]# docker run -d --name centos -v /home/centos:/home centos:latest
d63cf48430d21ca672cca359c1ca07315187fa759fef578f2e9256ed39d39f72
#通过docker inspect 容器ID 来查看挂载

实战mysql与主从复制

[root@docker ~]# docker images
REPOSITORY            TAG       IMAGE ID       CREATED         SIZE
mysql                 latest    3218b38490ce   17 months ago   516MB
#运行容器,做数据挂载
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:latest
[root@docker ~]# firewall-cmd --add-port=3310/tcp
success
[root@docker ~]# firewall-cmd --add-port=3310/tcp --permanent
success
[root@docker ~]# firewall-cmd --reload
success
[root@docker ~]# netstat -ntulp |grep 3310
tcp        0      0 0.0.0.0:3310            0.0.0.0:*               LISTEN      2054/docker-proxy
tcp6       0      0 :::3310                 :::*                    LISTEN      2058/docker-proxy
#启动成功之后,我们在本地使用数据库工具进行连接测试。

主从复制

MySQL复制数据流程
  1. 主库在数据更新提交事务之前,将事件异步记录到binlog二进制日志文件中,日志记录完成后存储引擎提交本次事务

  2. 从库启动一个I/O线程与主库建立连接,用来请求主库中要更新的binlog。这时主库创建的binlog dump线程,这是二进制转储线程,如果有新更新的事件,就通知I/O线程;当该线程转储二进制日志完成,没有新的日志时,该线程进入sleep状态。

  3. 从库的I/O线程接收到新的事件日志后,保存到自己的relay log(中继日志)中

  4. 从库的SQL线程读取中继日志中的事件,并执行更新保存。

在服务器创建两个目录 master 与 slave 分别对应主从数据库的data和conf ,目录结构如下
[root@docker ~]# tree /usr/local/master
/usr/local/master
├── conf
└── data2 directories, 0 files
[root@docker ~]# tree /usr/local/slave
/usr/local/slave
├── conf
└── data
[root@docker ~]# cat /usr/local/master/conf/my.cnf
[mysqld]
server-id=1024
## 开启二进制日志功能,可以随便取(关键)
log-bin=mysql-bin
secure_file_priv=/var/lib/mysql
default_authentication_plugin=mysql_native_password  #设置密码规则
max_connections=1000 #最大连接数设置 根据实际需要 自行调整[root@docker ~]# cat /usr/local/slave/conf/my.cnf
[mysqld]
## 设置server_id,注意要唯一
server-id=1022
## 开启二进制日志功能,以备Slave作为其它Slave的Master时使用
log-bin=mysql-slave-bin
## relay_log配置中继日志
relay_log=edu-mysql-relay-bin
secure_file_priv=/var/lib/mysql
default_authentication_plugin=mysql_native_password  #设置密码规则
max_connections=1000 #最大连接:数然后利用镜像分别启动两个容器,一个master 一个slave , master端口为3339,slave为3340 对应的root密码为123456,映射配置文件和数据存储目录到mysql
[root@docker ~]# docker run -d --name master -p 3339:3306 -v /usr/local/master/conf/my.cnf:/etc/mysql/my.cnf -v /usr/local/master/data/:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 -d mysql:latest
d64ca628a0330b2c5963ffa013949fd69646d4fc93c1497a2067ddf13e7ab830[root@docker ~]# docker run -d --name slave -p 3340:3306 -v /usr/local/slave/conf/my.cnf:/etc/mysql/my.cnf -v /usr/local/slave/data/:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 -d mysql:latest
876fd7a8c09672a0578d515775ee5ac50a4fc2669a92b14ced2e0fe9fa5c068c

主从数据同步配置

使用navicat连接 master数据库,进行slave账户创建及相关授权

mysql> create user 'slave'@'%' identified by '123456';
Query OK, 0 rows affected (0.04 sec)
mysql> grant replication slave, replication client on *.* to 'slave'@'%';
Query OK, 0 rows affected (0.01 sec)
#在Master进入mysql,执行show master status;
mysql> use mysql;
Database changed
mysql> show master status;
+------------------+----------+--------------+------------------+-------------------+
| File             | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
+------------------+----------+--------------+------------------+-------------------+
| mysql-bin.000003 |      673 |              |                  |                   |
+------------------+----------+--------------+------------------+-------------------+
1 row in set (0.03 sec)#回到linux服务器,执行命令查看master容器的ip地址
[root@docker ~]# docker inspect master --format={{.NetworkSettings.IPAddress}}
172.17.0.4
#然后在slave数据库服务器中根据上面得到的信息执行命令,主要master_log_file和 master_log_pos这两个参数需要根据上面语句查出来的结果进行配置,IP分配的内部地址基本就是172.17.0.2,如果不是这个地址改为 上述 查询出来的地址即可。
mysql> change master to master_host='172.17.0.4', master_user='slave', master_password='123456', master_port=3306, master_log_file='mysql-bin.000003', master_log_pos= 673, master_connect_retry=30;master_port: Master的端口号,指的是容器的端口号.
master_user:用于数据同步的用户
master_password:用于同步的用户的密码
master_log_file:指定Slave从哪个日志文件开始复制数据,即上文中提到的File字段的值
master_log_pos:从哪个Position开始读,即上文中提到的Position字段的值
master_connect_retry:如果连接失败,重试的时间间隔,单位是秒,默认是60秒[root@docker ~]# docker exec -it slave mysql -uroot -p123456
mysql> show slave status \G
Slave_IO_Running: No
Slave_SQL_Running: No
可以看到这两个属性都为No
设置从数据库开启主从服务
mysql> start slave;
Query OK, 0 rows affected, 1 warning (0.03 sec)
mysql> show slave status \G
*************************** 1. row ***************************Slave_IO_State: Waiting for source to send eventMaster_Host: 172.17.0.4Master_User: slaveMaster_Port: 3306Connect_Retry: 30Master_Log_File: mysql-bin.000003Read_Master_Log_Pos: 673Relay_Log_File: edu-mysql-relay-bin.000002Relay_Log_Pos: 324Relay_Master_Log_File: mysql-bin.000003Slave_IO_Running: YesSlave_SQL_Running: Yes

具名和匿名挂载

所谓匿名挂载(匿名卷),即在进行数据卷挂载的时候不指定宿主机的数据卷目录,-v命令之后直接跟上容器内数据卷所在的路径。

而具名挂载(命名卷)即在进行数据卷挂载的时候既指定宿主机数据卷所在路径,又指定容器数据卷所在路径。

命名卷在用过一次之后以后挂载容器的时候还是可以继续使用,所以一般在需要保存数据的时候使用命名卷的方式,匿名卷则是随着容器的建立而建立,随着容器的关闭而消亡。匿名卷一般用来存储无关痛痒的数据。

#匿名挂载
[root@docker ~]# docker run -d -P --name nginx01 -v /etc/nginx nginx:latest
#查看所有volume的情况
[root@docker ~]# docker volume ls
DRIVER    VOLUME NAME
local     0b2969a1a41591c0d8b9b1c5387727487362c5711a60ee92a2ded36f7d48a0a8
#我们在-v只写了容器内的路径,没有写宿主机的路径,这就是匿名挂载。[root@docker ~]# docker run -d -P --name nginx02 -v juming-nginx:/etc/nginx nginx
[root@docker ~]# docker volume ls
local     juming-nginx

所有docker容器内的卷,没有指定目录的情况下都是在/var/lib/docker/volumes

我们通过具名挂载可以方便找到我们的一个卷,大多数情况在使用的具名挂载

#如何确定具名挂载还是匿名挂载,还是指定路劲挂载
-v 容器内路径 #匿名挂载
-v 卷名:容器内路径#具名挂载
-v 宿主机路径:容器内路径 #指定路径挂载

拓展:

#通过-v 容器内路径:rw 改变读写权限
#一旦这个设置了容器权限,容器对我们挂载出来的内容有限定了。
docker run -d -P --name nginx02 -v juming-nginx:/etc/nginx:rw nginx
docker run -d -P --name nginx03 -v juming-nginx:/etc/nginx:ro nginx

数据卷容器

#通过--volumes-from继承父容器数据卷
[root@docker docker-test-volume]# docker run -it --name docker01 qhz/centos:1.0
[root@94918946324f /]# ls
bin  etc   lib    lost+found  mnt  proc  run   srv  tmp  var      volume02
dev  home  lib64  media       opt  root  sbin  sys  usr  volume01
[root@docker docker-test-volume]# docker run -it --name docker02 --volumes-from docker01 qhz/centos:1.0
[root@a2c49dcb063f /]# ls -l
total 0
lrwxrwxrwx.   1 root root   7 Nov  3  2020 bin -> usr/bin
drwxr-xr-x.   5 root root 360 Jun 15 02:45 dev
drwxr-xr-x.   1 root root  66 Jun 15 02:45 etc
drwxr-xr-x.   2 root root   6 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   6 Sep 15  2021 lost+found
drwxr-xr-x.   2 root root   6 Nov  3  2020 media
drwxr-xr-x.   2 root root   6 Nov  3  2020 mnt
drwxr-xr-x.   2 root root   6 Nov  3  2020 opt
dr-xr-xr-x. 223 root root   0 Jun 15 02:45 proc
dr-xr-x---.   2 root root 162 Sep 15  2021 root
drwxr-xr-x.  11 root root 163 Sep 15  2021 run
lrwxrwxrwx.   1 root root   8 Nov  3  2020 sbin -> usr/sbin
drwxr-xr-x.   2 root root   6 Nov  3  2020 srv
dr-xr-xr-x.  13 root root   0 Jun 15 00:59 sys
drwxrwxrwt.   7 root root 171 Sep 15  2021 tmp
drwxr-xr-x.  12 root root 144 Sep 15  2021 usr
drwxr-xr-x.  20 root root 262 Sep 15  2021 var
drwxr-xr-x.   2 root root   6 Jun 15 02:43 volume01
drwxr-xr-x.   2 root root   6 Jun 15 02:43 volume02[root@docker docker-test-volume]# docker exec -it docker01 /bin/bash
[root@94918946324f /]# cd volume01
[root@94918946324f volume01]# cd ..
[root@94918946324f /]# touch volume01/docker01
[root@94918946324f /]# exit
exit
[root@docker docker-test-volume]# docker exec -it docker02 /bin/bash
[root@a2c49dcb063f /]# ls volume01
docker01

dockerfile

dockerfile介绍

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

构建步骤:

1、编写一个dockerfile文件

2、docker build 构建成为一个镜像

3、docker run 运行镜像

4、docker push 发布镜像(DockerHub、阿里云镜像仓库)

dockerfile构建过程

基础知识

1、每个保留关键字(指令)都必须是大写字母

2、执行从上到下 依次执行

3、每一个指令都会创建提交一个新的镜像层,并提交。

dockerfile指令

FROM    #基础镜像
MAINTAINER    #镜像是谁写的,姓名加邮箱
RUN           #镜像构建的时候需要运行的命令
COPY          #拷贝dockerfile上下文中本机到容器内
ADD           #拷贝dockerfile上下文中本机、远程文件、需要自动解压的文件到容器内
WORKDIR       #镜像的工作目录
VOLUME        #挂载的目录
EXPOST        #保留端口配置
CMD           #指定这个容器启动的时候要运行的命令命令
ENTRYPOINT    #指定这个容器启动的时候要运行的命令,可以追加
ONBUILD       #当构建一个被继承dockerfile这个时候就会运行ONBUILD指令,触发指令
ARG           #定义构建参数,可以在构建命令docker build中用--build-<参数名>=<值> 来覆盖
ENV           #构建的时候设置环境变量
USER          #指定容器启动的用户
HEALTHCHECK   #设置检查容器健康状态
SHELL         #指定RUN、ENTRYPOINT、CMD指令的shell,linux默认/bin/bash
LABEL         #用来给镜像以键值对的形式添加一些元数据(metadata)

FROM

FROM [--platform=<platform>] <image> [AS <name>]
FROM [--platform=<platform>] <image>[:<tag>] [AS <name>]
FROM [--platform=<platform>] <image>[@<digest>] [AS <name>]

FROM 指令初始化一个新的构建阶段,并为后续指令设置基础镜像,该镜像可以是任何有效的镜像。举个例子:

FROM centos:7
...

需要注意的是:

  • 一般来说,Dockerfile都是以FROM指令开始;但ARG是Dockerfile中唯一可能先于FROM的指令(具体参考下面的ARG介绍)。
  • FROM可以在一个Dockerfile中出现多次,以创建多个映像或使用一个构建阶段作为另一个构建阶段的依赖项。只需在每个新的FROM指令之前记录提交的最后一个图像ID输出。每个FROM指令清除前面指令创建的任何状态。
  • [AS <name>] 参数可以添加在FROM指令之后,来为新的构建阶段指定一个名称。该名称可以在后续的FROMCOPY ——FROM =<name>指令中使用,以引用在此阶段构建的映像。
  • tagdigest值是可选的。如果省略其中任何一个,构造器默认采用latest标记。如果不能找到tag值,构造器将返回一个错误。
  • --platform标志可用于在FROM引用多平台镜像的情况下指定镜像的平台。如:linux/amd64, linux/arm64, or windows/amd64
  • 除了选择现有镜像为基础镜像外,Docker 还存在一个特殊的镜像,名为 scratch。这个镜像是虚拟的概念,并不实际存在,它表示一个空白的镜像。

MAINTAINER

MAINTAINER <name>

MAINTAINER指令设置生成的镜像的Author字段,已经废弃使用。LABEL指令是一个更灵活的版本,你、应该使用它,因为它可以设置所需要的任何元数据,并且可以很容易地通过docker inspect查看。可以使用与MAINTAINER字段相对应的标签:

LABEL org.opencontainers.image.authors="SvenDowideit@home.org.au"

在下面将会介绍LABEL指令。

RUN

RUN指令将在当前镜像上的新层中执行任何命令并提交结果。生成的提交镜像将用于Dockerfile中的下一步。其格式有两种:

  • shell 格式:RUN <命令>,就像直接在命令行中输入的命令一样。刚才写的 Dockerfile 中的 RUN 指令就是这种格式。
  • exec 格式:RUN ["可执行文件", "参数1", "参数2"],这更像是函数调用中的格式。

需要注意的是,Dockerfile 中每一个指令都会建立一层,RUN 也不例外。每一个 RUN 的行为,就会新建立一层,在其上执行这些命令,执行结束后,commit 这一层的修改,构成新的镜像。

在shell形式中,可以使用(反斜杠)来将单个RUN指令继续到下一行。例如这两行:

RUN /bin/bash -c 'source $HOME/.bashrc; \
echo $HOME'

等价于

RUN /bin/bash -c 'source $HOME/.bashrc; echo $HOME'

COPY

COPY [--chown=<user>:<group>] <源路径>... <目标路径>
COPY [--chown=<user>:<group>] ["<源路径1>",... "<目标路径>"]

RUN 指令一样,也有两种格式,一种类似于命令行,一种类似于函数调用。COPY 指令将从构建上下文目录中 <源路径> 的文件/目录复制到新的一层的镜像内的 <目标路径> 位置。比如:

COPY package.json /usr/src/app/

<源路径> 可以是多个,甚至可以是通配符,其通配符规则要满足 Go 的 filepath.Match 规则,如:

COPY hom* /mydir/
COPY hom?.txt /mydir/

<目标路径> 可以是容器内的绝对路径,也可以是相对于工作目录的相对路径(工作目录可以用 WORKDIR 指令来指定)。目标路径不需要事先创建,如果目录不存在会在复制文件前先行创建缺失目录。

--chown=<user>:<group> 选项用来改变文件的所属用户及所属组。

COPY --chown=55:mygroup files* /mydir/
COPY --chown=bin files* /mydir/
COPY --chown=1 files* /mydir/
COPY --chown=10:11 files* /mydir/

注意:如果源路径为文件夹,复制的时候不是直接复制该文件夹,而是将文件夹中的内容复制到目标路径。

ADD

ADD 指令和 COPY 的格式和性质基本一致。但是在 COPY 基础上增加了一些功能。

比如 <源路径> 可以是一个 URL,这种情况下,Docker 引擎会试图去下载这个链接的文件放到 <目标路径> 去。下载后的文件权限自动设置为 600,如果这并不是想要的权限,那么还需要增加额外的一层 RUN 进行权限调整,另外,如果下载的是个压缩包,需要解压缩,也一样还需要额外的一层 RUN 指令进行解压缩。所以不如直接使用 RUN 指令,然后使用 wget 或者 curl 工具下载,处理权限、解压缩、然后清理无用文件更合理。因此,这个功能其实并不实用,而且不推荐使用。

如果 <源路径> 为一个 tar 压缩文件的话,压缩格式为 gzip, bzip2 以及 xz 的情况下,ADD 指令将会自动解压缩这个压缩文件到 <目标路径> 去。

在某些情况下,这个自动解压缩的功能非常有用,比如官方镜像 ubuntu 中:

FROM scratch
ADD ubuntu-xenial-core-cloudimg-amd64-root.tar.gz /
...

同样,在使用该指令的时候还可以加上 --chown=<user>:<group> 选项来改变文件的所属用户及所属组。

COPY vs ADD

在 Docker 官方的 Dockerfile 最佳实践文档 中要求,尽可能的使用 COPY,因为 COPY 的语义很明确,就是复制文件而已,而 ADD 则包含了更复杂的功能,其行为也不一定很清晰。最适合使用 ADD 的场合,就是所提及的需要自动解压缩的场合。

另外需要注意的是,ADD 指令会令镜像构建缓存失效,从而可能会令镜像构建变得比较缓慢。

因此在 COPYADD 指令中选择的时候,可以遵循这样的原则,所有的文件复制均使用 COPY 指令,仅在需要自动解压缩的场合使用 ADD

CMD

The CMD 指令的三种格式:

CMD ["executable","param1","param2"] (exec 格式, 首选推荐)
CMD ["param1","param2"] (在指定了 ENTRYPOINT 指令后,用 CMD 指定具体的参数,下面介绍ENTRYPOINT指令)
CMD command param1 param2 (shell 格式)

Docker 不是虚拟机,容器就是进程。既然是进程,那么在启动容器的时候,需要指定所运行的程序及参数。CMD 指令就是用于指定默认的容器主进程的启动命令的。

在运行时可以指定新的命令来替代镜像设置中的这个默认命令,比如,ubuntu 镜像默认的 CMD/bin/bash,如果我们直接 docker run -it ubuntu 的话,会直接进入 bash。我们也可以在运行时指定运行别的命令,如 docker run -it ubuntu cat /etc/os-release。这就是用 cat /etc/os-release 命令替换了默认的 /bin/bash 命令了,输出了系统版本信息。

在指令格式上,一般推荐使用 exec 格式,如果使用 shell 格式的话,实际的命令会被包装为 sh -c 的参数的形式进行执行。比如:

CMD echo $HOME

在实际执行中,会将其变更为:

CMD [ "sh", "-c", "echo $HOME" ]

**注意1:**容器中应用应在前台执行;原因:Docker 不是虚拟机,容器就是进程。如下面Nginx容器启动的命令。

无效的命令(容器执行后就立即退出,原因容器主进程结束):

CMD service nginx start

有效命令(前台方式运行,不结束主进程):

CMD ["nginx", "-g", "daemon off;"]

**注意2:**一个Dockerfile应该仅有一个CMD指令,当存在多个,仅最后一个生效(以最后一个为准)。

ENTRYPOINT

ENTRYPOINT 的格式和 RUN 指令格式一样,分为 exec 格式和 shell 格式。

ENTRYPOINT 的目的和 CMD 一样,都是在指定容器启动程序及参数。ENTRYPOINT 在运行时也可以替代,不过比 CMD 要略显繁琐,需要通过 docker run 的参数 --entrypoint 来指定。

当指定了 ENTRYPOINT 后,CMD 的含义就发生了改变,不再是直接的运行其命令,而是将 CMD 的内容作为参数传给 ENTRYPOINT 指令,换句话说实际执行时,将变为:

<ENTRYPOINT> "<CMD>"

那么有了 CMD 后,为什么还要有 ENTRYPOINT 呢?这种 <ENTRYPOINT> "<CMD>" 到底是什么意思,有什么好处?

举个例子

docker概念和基础命令相关推荐

  1. Docker、docker安装及基础命令

    Docker是什么? Docker是一个在2013年开源的应用程序,并且是一个基于go语言编写的PAAS服务. Docker最早采用LXC技术,之后改为自己研发并开源的runc技术运行容器. Dock ...

  2. rabbitmq基础2——rabbitmq二进制安装和docker安装、基础命令

    文章目录 一.RabbitMQ安装 1.1 二进制安装 1.2 rabbitmqctl工具 1.3 docker安装 二.rabbitmq基础命令 2.1 多租户与权限类 2.1.1 创建虚拟主机 2 ...

  3. 在虚拟机安装中Docker及一些基础命令

    一.安装 yum -y update yum install -y docker docker version 查看docker版本信息 安装成功如上图. 二.基础命令 1.docker pull 镜 ...

  4. docker几个基础命令及nodejs容器

    最近在了解docker使用,在网上找资料发现很少有可以系统的了解某一个操作目的的大部分细节的参考,学起来非常吃力.耗时. 这里整理了一下自己在入门摸索过程中的几个入门级的操作,可以连贯的对实现一个no ...

  5. docker入门之基础命令

    安装 更新yum到最新版本 yum update 下载依赖,yum-util 提供yum-config-manage功能,另外则是device-mapper驱动依赖 yum install -y yu ...

  6. Kubernetes与Docker基本概念与常用命令对照

    摘要: Docker是众多用户上手入门的基础容器和编排工具,提供了良好的开发者体验.Kubernetes是强大的容器编排平台,功能丰富.它们有很多概念和操作都有类似之处.我们今天会和大家对比基本概念与 ...

  7. Docker笔记1 基础概念和镜像

    基础概念 开发环境deepin linux 15.11.安装docker之后,执行命令: sudo usermod -aG docker $USER_NAME 让docker和当前用户在一个组中,可以 ...

  8. Docker基础命令学习

    Docker基础命令学习 二.Docker 入门 Docker是什么? Docker 是一个开源的应用容器引擎,你可以将其理解为一个轻量级的虚拟机,开发者可以打包他们的应用以及依赖包到一个可移植的容器 ...

  9. Docker概述、安装及基础命令

    Docker概述.安装及基础命令 一.Docker概述 1. docker是什么 2. docker与虚拟机的区别 3. docker使用场景 4. docker核心概念 5. docker引擎 6. ...

最新文章

  1. 用 Python 画自画像?看完我笑了
  2. 另类设计:12个基于桌面视图的网页设计作品
  3. python创造订单_Odoo 10根据销售订单创建项目
  4. Python中文件操作和文件夹操作的学习笔记
  5. 客服机器人代码_企业微信群机器人如何快速集成?无需开发连接微信公众号,表单系统,钉钉,推广,CRM,客服系统和数据库...
  6. turnserver.conf文件详解
  7. [转载]下载网页中的ts视频文件
  8. 工业视觉检测厂家十大排名
  9. VC网络编程(Socket)项目化视频教程
  10. UVA - 10827 Maximum sum on a torus
  11. 13.tornado操作之增加用户喜欢的图片展示页+同时展示用户上传的所有图片增加展示图片有多少用户喜欢的功能
  12. 关于5G通信技术,你了解多少?
  13. 28 关于 Finalizer
  14. HYSBZ 2565 最长双回文串 (回文树)
  15. 【面经】三七互娱Java游戏研发实习(一面)
  16. 超级总结:vs2008/2005 sp1 C++ 发布程序 .
  17. 轻松玩转新编日语1 zz江沪
  18. Audition Au制作左右双声道以及导出
  19. 苹果试玩换个新id行不_【图】- 日照市苹果app换个id重复刷试玩能不能 - 昆山玉山其他服务 - 昆山百姓网...
  20. 基于复杂网络理论的计算机网络拓补分析,基于复杂网络理论的计算机网络拓扑分析论文.doc...

热门文章

  1. [NewLife.XCode]实体队列(多线程生产的大数据集中保存)
  2. JS节点操作(1) - 父节点,子节点,兄弟节点
  3. 工业智能网关实现工业设备数据采集和状态监控
  4. oracle命令解锁用户,在命令行下进行Oracle用户解锁
  5. HTML常用标签以及特殊符号
  6. 基于51单片机的实时时钟设计
  7. 高性能网站建设指南---前端工程师技能精髓
  8. delphi html 表格控件,Delphi TTable 组件
  9. CUDA学习(十):向量内积的多种方法实现
  10. 如何做好抖音直播,实现高效引流变现?