最新要闻
- 【世界新视野】抖音打不开视频怎么回事_抖音打不开
- 当前要闻:山姆同款蛋糕杭州卖165上海卖95 网友直呼太坑:你遇到过吗
- 绵云般口感!和路雪千层雪冰淇淋3.5元官方大促(商超6元)
- 今年唯一LCD性能机!Redmi Note 12T Pro外观公布-当前独家
- 重点聚焦!研究称每天排便超一次或影响健康:心力衰竭风险增加33%
- 全球快资讯丨接近小米1英寸了!曝iPhone 16 Pro Max主摄大升级
- “候鸟”老人现在是否可以在海南澄迈县买房子?深蓝苑·滨江城PK五指山森林湖养老分析!|焦点简讯
- 神舟十六号30日9时31分发射:三名航天员名单公布 首次有大学教授
- 华为畅享60X根治续航焦虑!充电宝完全派不上用场了
- 全球微资讯!华为智选车终于出轿车 奇瑞EH3谍照曝光:华为ADS摄像头瞩目
- 世界今热点:小屏4K“天花板”优派推出新款23.8英寸显示器 1999元
- 神十六乘组公布:载荷专家将首登天宫-动态
- 搜狐汽车全球快讯 | 比亚迪或考虑在法国建厂 比亚迪:正评估建厂可行性-全球资讯
- 618开车神价 爱国者4TB PCIe 4.0硬盘999元(国产长寿TLC闪存)-全球今亮点
- 特斯拉、丰田、BBA等都靠边!离开中国:世界无法生产电动车电池 当前观点
- 苹果iPhone销冠地位稳如泰山 今年618攻略请收好
手机
iphone11大小尺寸是多少?苹果iPhone11和iPhone13的区别是什么?
警方通报辅警执法直播中被撞飞:犯罪嫌疑人已投案
- iphone11大小尺寸是多少?苹果iPhone11和iPhone13的区别是什么?
- 警方通报辅警执法直播中被撞飞:犯罪嫌疑人已投案
- 男子被关545天申国赔:获赔18万多 驳回精神抚慰金
- 3天内26名本土感染者,辽宁确诊人数已超安徽
- 广西柳州一男子因纠纷杀害三人后自首
- 洱海坠机4名机组人员被批准为烈士 数千干部群众悼念
家电
Docker 学习笔记
Docker 学习笔记
这篇学习笔记将用于记录本人在学习 Docker 服务端运维工具过程中所记录的心得体会,它将会被存储在https://github.com/owlman/study_note
项目的SoftwareTool/Container
目录下一个名为的Docker
子目录中。
学习规划
- 学习基础:
- 有一两门编程语言的使用经验。
- 有一定的 Web 开发及维护经验。
- 视频资料:
- 黑马程序员 Docker 容器化技术:哔哩哔哩上的视频教程。
- 阅读资料:
- 《深入浅出 Docker》:本人学习所用书籍。
- 学习目标:
- 使用 Docker 发布并维护自己的私人项目.
Docker 简介
和许多成功的软件项目都有一个无心插柳柳成荫的故事一样,Docker 原本只是一家名为 dotCloud 的 PaaS 服务提供商启动的一个业余项目,该项目在开源之后意外获得了巨大的成功,以至于 dotCloud 公司干脆放弃了原本就不景气的 PaaS 业务,并且将公司改名为 Docker Inc,以便专职维护这个项目。该项目如今的正式名称叫 Moby,读者可以在 GitHub 上找到它。
(资料图)
Docker 这个词在英文中的意思是“码头工人”,这一工种的主要工作是装卸货船上的集装箱,因此该运维工具的核心工作理念就是让应用程序在服务器上的部署像装卸集装箱一样,实现标准化的组件式管理,业界称这种工作理念为容器化部署。从概念上来看,容器的概念和传统的虚拟机比较类似,它们之间主要存在着以下区别:
- 虚拟机依赖的是计算机硬件层面上的技术,而容器是构建在操作系统层面上的,它复用的是操作系统的容器化技术。
- 虚拟机中部署的是一个完整的操作系统,而容器中封装的只是一个与指定应用程序相关的操作系统子集,相对更为轻量化。
- 虚拟机通常是通过快照来保存其运行状态的;而容器则引入了类似于版本控制系统的机制,这种机制可以让运维人员更方便、快速地将应用程序的运行状态切换到其之前的某个历史时间节点上。
以上不同之处也解释了我们为什么需要使用 Docker 这样的工具来对服务端的应用程序进行容器化部署。试想一下,如果我们基于 Vue.js 前端框架、Express.js 后端框架以及 MongoDB 数据库开发了一个 Web 应用程序,而这些应用程序框架和数据库的版本通常是日新月异,不同版本之间内部实现的变化有时也非常剧烈,很多时候基于前一个版本可用的代码,到了下一个版本就运行出错了。这就要求我们在最终部署应用程序的时候在服务器上安装指定版本的框架和数据库,这将是一个非常耗时费力且容易出错的工作。而且一旦遇到服务器故障,应用迁移等问题,这一切工作又得重来一遍,其运维成本可想而知。而容器的作用就是能将应用程序与其所依赖的框架、数据库、操作系统固化下来。
Docker 本质上就是这样一个基于Linux容器(Linux Containers,简称 LXC)技术实现的容器管理引擎。它会通过应用程序及所有程序的依赖环境打包到一个虚拟容器中,这个虚拟容器可以运行在任何一台安装了 Docker 容器引擎的服务器设备上,无论该设备是一台实体的物理设备、还是无实体的云主机或本地虚拟机,都不会影响我们部署容器内的应用程序。这样一来,我们就可以在任何主流的操作系统中对服务端的应用程序进行开发、调试和运行,而不必担心它的可移植性了。
安装 Docker
在正式安装 Docker 之前,我们首先要了解一下该产品所发布的各种版本。和所有追求盈利的软件公司一样,随着产品在市场上的不断流行与发展,docker Inc 公司也不能免俗地开启了将产品商业化的道路。于是,Docker 自 17.03 这个版本之后就被分成了 CE(Community Edition,即社区版)和 EE(Enterprise Edition,即企业版)两种不同的版本。其中,Docker CE 是保持免费的版本,它包含了完整的 Docker 平台,非常适合开发人员和运维团队构建用于部署指定应用程序的容器。值得一提的是,Docker CE 本身也还被分成了以下两个版本:
- edge 版本每月发布一次,只提供一个月的支持和维护期,主要面向那些热衷于研究 Docker 本身,喜欢尝试新功能的用户。
- stable 版本每季度发布一次,将提供四个月的支持和维护期,适用于希望在具体工作中对一些实际项目进行维护的用户。
而 Docker EE 的发布节奏则与 Docker CE 的 stable 版本基本保持一致,但每个 Docker EE 版本都享受为期一年的支持与维护期,在此期间接受安全与关键修正。总而言之,Docker CE 并非是功能上的阉割版,而 Docker EE 则只是面前企业用户增加了收费的维护服务以及一些周边产品,以求进一步降低企业运营的风险,但它们在个人的学习体验上不会有太大的区别。
在这里,我们将会主要以 Docker CE 为主来展开针对容器化部署议题的探讨,因此接下来的任务就是要在一个之前配置好的 Ubuntu 系统中安装 Docker CE。为此,我们需要执行以下步骤。
首先要做的是将 Docker 所在的 APT 软件源添加到 Ubuntu 的 APT 列表中,为此,我们需要先更新一下当前的软件包索引,并安装一些基础工具:
sudo apt updatesudo apt install \ apt-transport-https \ ca-certificates \ curl \ gnupg \ lsb-release
接下来,我们需要使用 curl 工具导入 Docker APT 软件源的 GPG 密钥:
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
现在,我们就可以通过以下命令正式地将 Docker APT 软件源添加到 Ubuntu 的 APT 列表中:
sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"# `sb_release -cs`变量表达式返回的是Ubuntu的版本代号,在这里是focal。
最后,我们需要再次更新一下系统的软件包索引,然后就可以安装 Docker CE 了,其安装命令如下:
sudo apt updatesudo apt install \ docker-ce \ docker-ce-cli \ containerd.io
当然了,以上命令安装的是 Docker APT 软件源中的最新版本,如果我们想安装的是 Docker 的某个指定版本,需要先执行apt list -a docker-ce
命令获取到 Docker APT 软件源中所有可用的版本,例如像这样:
$ apt list -a docker-ceListing...docker-ce/focal,now 5:20.10.12~3-0~ubuntu-focal amd64 [installed]docker-ce/focal 5:20.10.11~3-0~ubuntu-focal amd64docker-ce/focal 5:20.10.10~3-0~ubuntu-focal amd64docker-ce/focal 5:20.10.9~3-0~ubuntu-focal amd64docker-ce/focal 5:20.10.8~3-0~ubuntu-focal amd64docker-ce/focal 5:20.10.7~3-0~ubuntu-focal amd64docker-ce/focal 5:20.10.6~3-0~ubuntu-focal amd64docker-ce/focal 5:20.10.5~3-0~ubuntu-focal amd64docker-ce/focal 5:20.10.4~3-0~ubuntu-focal amd64docker-ce/focal 5:20.10.3~3-0~ubuntu-focal amd64docker-ce/focal 5:20.10.2~3-0~ubuntu-focal amd64docker-ce/focal 5:20.10.1~3-0~ubuntu-focal amd64docker-ce/focal 5:20.10.0~3-0~ubuntu-focal amd64docker-ce/focal 5:19.03.15~3-0~ubuntu-focal amd64docker-ce/focal 5:19.03.14~3-0~ubuntu-focal amd64docker-ce/focal 5:19.03.13~3-0~ubuntu-focal amd64docker-ce/focal 5:19.03.12~3-0~ubuntu-focal amd64docker-ce/focal 5:19.03.11~3-0~ubuntu-focal amd64docker-ce/focal 5:19.03.10~3-0~ubuntu-focal amd64docker-ce/focal 5:19.03.9~3-0~ubuntu-focal amd64
然后根据该命令列出的可用版本,执行以下命令来安装:
# 通过在软件包名后面添加""=<版本号>"的方式来安装指定版本:sudo apt install \ docker-ce=<版本号> \ docker-ce-cli=<版本号> \ containerd.io
使用APT软件源来安装软件的另一个好处是,当新版本的 Docker CE 发布时,我们可以直接通过sudo apt update && sudo apt upgrade
命令来进行自动升级。当然了,如果想阻止 Docker 的自动更新,我们也可以通过执行以下命令来锁住它的版本:
sudo apt-mark hold docker-ce
配置工作
在基于 Debian 项目的 Linux 发行版上,docker在被安装只会通常会被自动设置为系统的开机启动服务。当然了,如果需要的话,我们也可以通过执行以下命令手动该服务设置为系统的开机启动项。
sudo systemctl enable docker
在一切安装妥当之后,我们可以通过以下这命令来查看 Docker 的版本并确认该服务是否已被启动:
$ docker version Client: Docker Engine - Community Version: 20.10.12 API version: 1.41 Go version: go1.16.12 Git commit: e91ed57 Built: Mon Dec 13 11:45:33 2021 OS/Arch: linux/amd64 Context: default Experimental: true$ sudo service docker status * Docker is running
另外,由于在默认情况下,只有root
用户或有sudo
权限的用户可以执行Docker操作,所以如果我们平时使用非root
用户,但又不想每次执行Docker操作的时候都得在相关命令之前加上sudo
前缀,也可以选择添加一个docker
用户组,并将我们使用的非root用户加入到该组中,其具体命令如下:
$ sudo groupadd docker$ sudo usermod -aG docker $USER# 这里的$USER是一个环境变量,代表当前用户名。
如果我们想要确认一下 Docker 的容器管理功能是否已经可供使用,可以试着执行以下命令运行一个测试容器:
$ docker container run hello-worldHello 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 bashShare images, automate workflows, and more with a free Docker ID: https://hub.docker.com/For more examples and ideas, visit: https://docs.docker.com/get-started/
上述命令将会从 Docker Hub 中下载一个名为hello-world
测试镜像,并根据该镜像实例化一个测试容器。而该容器中的应用程序会在运行时打印出带有“Hello from Docker”字样等相关内容的信息之后退出。
镜像与容器
正如我们之前所说, Docker 本质上就是一个用于管理容器的服务端工具。而其中用于创建容器的模板,我们就称之为容器的镜像,其作用与我们在使用 Vmware 或 VirtualBox 之类的虚拟机管理器创建虚拟机时选择的模板或快照基本相同(例如我们要创建的是 Linux 系统的虚拟机还是 Windows 系统的虚拟机,抑或是一个安装了 Node.js 的主机),或者如果熟悉面向对象思想的话,也可以将 Docker 中的容器理解为程序在运行过程中存在于内存中的对象实体,而容器就是我们用于创建这些对象的类。
理解镜像
简而言之,镜像就是在某一刻停止运行的容器快照。例如我们可以将一个运行了 Ubuntu 系统的容器创建成一个镜像,而将这个容器安装了 Node.js 之后的状态创建为另一个镜像。这样一来,当我们需要一个运行了纯净Ubuntu 环境的容器时,就可以使用第一个镜像来创建它,而当我们需要一个安装在 Ubuntu 上的 Node.js 运行环境时就可以使用第二个镜像来创建容器。同样的,当我们 在Node.js 运行环境中创建了一个引入 Express.js 框架的项目,还可以继续将其创建为一个镜像,以后在需要启动一个 Express 项目的时候,也可以用该镜像快速创建一个项目开发和运维环境。
而基于上述使用镜像的方式,Docker 中镜像在存储上被设计成了分层叠加的结构,并且这些分层是可以在镜像之间共享的,例如在上述三个镜像中,三个镜像之间可以共享 Ubuntu 所在的分层,而后两个镜像也可以共享 Ubuntu 和 Node.js 两个分层。这样一来,这三个镜像在同一主机上整体所占的空间会大幅减少,我们在将它们推送到镜像仓库或者从镜像仓库中拉取它们时,很多时候是不必传输重复的分层的,这也是容器在运维工作上优于虚拟机的原因之一。
而容器相较于虚拟机的另一个优势则在于,即使容器镜像中包含了 Ubuntu 这类操作系统,它通常也只封装了该操作系统的文件系统和一个精简的 Shell 程序,并不包含与任何硬件驱动相关的内核部分。它是与宿主机器共享操作系统内核的。因此与完整的虚拟机相比,显然体积更为轻量化。例如,Docker 官方发布的 Ubuntu 镜像大约只有 80MB 左右的大小,而一个安装了 Ubuntu 系统的虚拟机则通常有 8GB 左右的大小。
镜像操作
接下来,让我们来具体介绍一下如何在 Docker 中进行镜像操作。在默认情况下,如果我们是在类 Linux 系统中安装的 Docker,其本地镜像的存储位置通常位于/var/lib/docker/
目录中,如果是在 Windows 主机上安装的,本地镜像就应存储在C:\ProgramData\docker\windowsfilter
目录中。读者可以使用docker image ls
命令来查看当前本地镜像列表,像这样:
$ docker image ls REPOSITORY TAG IMAGE ID CREATED SIZEhello-world latest feb5d9fea6a5 6 months ago 13.3kB
当然了,我们在刚刚安装完 Docker 时本地应该是没有任何镜像的,但由于之前为了测试安装是否正确,我们已经从 Docker Hub 中下载了一个名为hello-world
测试镜像,所以读者会在上述镜像列表中看到它。在专业术语中,大家将镜像从远程仓库服务中下载到本地的操作称之为拉取(pull)。现在,如果读者想要拉取一个最新版本的 Ubuntu 镜像,就需要执行以下操作将它拉取到本地:
$ docker image pull ubuntu:latest latest: Pulling from library/ubuntue0b25ef51634: Pulling fs layere0b25ef51634: Download completee0b25ef51634: Pull completeDigest: sha256:9101220a875cee98b016668342c489ff0674f247f6ca20dfc91b91c0f28581aeStatus: Downloaded newer image for ubuntu:latestdocker.io/library/ubuntu:latest$ docker image ls REPOSITORY TAG IMAGE ID CREATED SIZEubuntu latest 825d55fb6340 6 days ago 72.8MBhello-world latest feb5d9fea6a5 6 months ago 13.3kB
如你所见,docker image pull <远程仓库名>:<版本标签>
命令会负责将指定的镜像从远程镜像仓库服务的仓库中拉取到本地。在默认情况下,Docker 所使用的是其官方的远程镜像仓库服务 Docker Hub。具体到上述操作中,docker image pull ubuntu:latest
命令的作用就是去 Docker Hub 将 Ubuntu 仓库中标签为 latest 的容器镜像拉取到本地。而通过docker image ls
命令,我们可以看到,该镜像的大小只有 72.8MB。另外,关于拉取镜像的命令,我们还需要注意以下几点。
- 如果我们在执行拉取命令时没有在仓库名称后指定具体的版本标签,则 Docker 会默认拉取标签为 latest 的镜像。例如,我们之前在拉取 Ubuntu 镜像时,拉取命令也可以简写为
docker image pull ubuntu:latest
,效果是完全一样的。 - 标签为 latest 的镜像是 Docker 默认要拉取的镜像,但并不保证该镜像是仓库中最新版本的镜像。例如,Alpine 仓库中最新镜像的标签通常是 edge。所以,希望读者使用 latest 标签时谨慎行事。
当然了,如果我们不知道远程仓库服务中有哪一些远程仓库可供使用,也可以使用docker search <关键字>
命令进行查询。例如在下面的操作中,我们对 Docker Hub 中存有的所有与 Ubuntu 相关的远程仓库进行了查询。
$ docker search ubuntuNAME DESCRIPTION STARS OFFICIAL AUTOMATEDubuntu Ubuntu is a Debian-based Linux operating sys… 14048 [OK] websphere-liberty WebSphere Liberty multi-architecture images … 283 [OK] ubuntu-upstart DEPRECATED, as is Upstart (find other proces… 112 [OK] neurodebian NeuroDebian provides neuroscience research s… 88 [OK] open-liberty Open Liberty multi-architecture images based… 52 [OK] ubuntu-debootstrap DEPRECATED; use "ubuntu" instead 46 [OK] ubuntu/nginx Nginx, a high-performance reverse proxy & we… 40 ubuntu/mysql MySQL open source fast, stable, multi-thread… 29 ubuntu/apache2 Apache, a secure & extensible open-source HT… 26 ubuntu/prometheus Prometheus is a systems and service monitori… 23 kasmweb/ubuntu-bionic-desktop Ubuntu productivity desktop for Kasm Workspa… 22 ubuntu/squid Squid is a caching proxy for the Web. Long-t… 18 ubuntu/postgres PostgreSQL is an open source object-relation… 15 ubuntu/bind9 BIND 9 is a very flexible, full-featured DNS… 13 ubuntu/redis Redis, an open source key-value store. Long-… 9 ubuntu/prometheus-alertmanager Alertmanager handles client alerts from Prom… 5 ubuntu/grafana Grafana, a feature rich metrics dashboard & … 5 ubuntu/memcached Memcached, in-memory keyvalue store for smal… 4 ubuntu/telegraf Telegraf collects, processes, aggregates & w… 3 circleci/ubuntu-server This image is for internal use 3 ubuntu/cortex Cortex provides storage for Prometheus. Long… 2 ubuntu/cassandra Cassandra, an open source NoSQL distributed … 1 bitnami/ubuntu-base-buildpack Ubuntu base compilation image 0 [OK]snyk/ubuntu A base ubuntu image for all broker clients t… 0 rancher/ubuntuconsole 0
值得注意的是,在默认情况下,docker search
命令通常只返回 25 条结果。但是,读者可以通过设置--limit
参数的值来指定该命令返回的条目数,最多可设置为 100 条。
在将镜像拉取到本地之后,我们可以使用docker image inspect
命令来查看镜像中的各种细节,包括镜像层数据和元数据。例如在下面的操作中,我们使用该命令查看了hello-world
测试镜像中的细节。
$ docker image inspect hello-world:latest[ { "Id": "sha256:feb5d9fea6a5e9606aa995e879d862b825965ba48de054caab5ef356dc6b3412", "RepoTags": [ "hello-world:latest" ], "RepoDigests": [ "hello-world@sha256:97a379f4f88575512824f3b352bc03cd75e239179eea0fecc38e597b2209f49a" ], "Parent": "", "Comment": "", "Created": "2021-09-23T23:47:57.442225064Z", "Container": "8746661ca3c2f215da94e6d3f7dfdcafaff5ec0b21c9aff6af3dc379a82fbc72", "ContainerConfig": { "Hostname": "8746661ca3c2", "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", "#(nop) ", "CMD [\"/hello\"]" ], "Image": "sha256:b9935d4e8431fb1a7f0989304ec86b3329a99a25f5efdc7f09f3f8c41434ca6d", "Volumes": null, "WorkingDir": "", "Entrypoint": null, "OnBuild": null, "Labels": {} }, "DockerVersion": "20.10.7", "Author": "", "Config": { "Hostname": "", "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": [ "/hello" ], "Image": "sha256:b9935d4e8431fb1a7f0989304ec86b3329a99a25f5efdc7f09f3f8c41434ca6d", "Volumes": null, "WorkingDir": "", "Entrypoint": null, "OnBuild": null, "Labels": null }, "Architecture": "amd64", "Os": "linux", "Size": 13256, "VirtualSize": 13256, "GraphDriver": { "Data": { "MergedDir": "/var/lib/docker/overlay2/3a0e1e1bea4d0ac0bb55bb22f831cd7b6be43b33d5bb07203e8dc6ab0e5afc40/merged", "UpperDir": "/var/lib/docker/overlay2/3a0e1e1bea4d0ac0bb55bb22f831cd7b6be43b33d5bb07203e8dc6ab0e5afc40/diff", "WorkDir": "/var/lib/docker/overlay2/3a0e1e1bea4d0ac0bb55bb22f831cd7b6be43b33d5bb07203e8dc6ab0e5afc40/work" }, "Name": "overlay2" }, "RootFS": { "Type": "layers", "Layers": [ "sha256:e07ee1baac5fae6a26f30cabfe54a36d3402f96afda318fe0a96cec4ca393359" ] }, "Metadata": { "LastTagTime": "0001-01-01T00:00:00Z" } }]
从上述信息中,我们可以看出hello-world
测试镜像要运行的容器是一个基于 Linux 系统的,运行于 shell 终端环境中的一个 Hello World 程序。
最后,当我们不再需要某个镜像的时候,可以通过docker image rm
命令将该镜像从本地删除,该操作会在当前主机上删除指定的镜像以及相关的镜像层。这意味着我们之后见无法通过docker image ls
命令看到被删除的镜像,并且对应镜像分层数据所在的目录也会随之被删除。当然了,如果某个镜像分层被多个镜像共享,那只有当全部依赖该分层的镜像都被删除后,它才会被删除。在下面的示例中,我们将通过镜像ID来删除镜像。
$ docker image ls REPOSITORY TAG IMAGE ID CREATED SIZEubuntu latest 825d55fb6340 6 days ago 72.8MBhello-world latest feb5d9fea6a5 6 months ago 13.3kB$ docker image rm feb5dUntagged: hello-world:latestUntagged: hello-world@sha256:97a379f4f88575512824f3b352bc03cd75e239179eea0fecc38e597b2209f49aDeleted: sha256:feb5d9fea6a5e9606aa995e879d862b825965ba48de054caab5ef356dc6b3412Deleted: sha256:e07ee1baac5fae6a26f30cabfe54a36d3402f96afda318fe0a96cec4ca393359$ docker image ls REPOSITORY TAG IMAGE ID CREATED SIZEubuntu latest 825d55fb6340 6 days ago 72.8MB
需要注意的是,如果被删除的镜像已经在本地实例化出了若干个容器,那么在这些容器被删除之前,该镜像是无法被删除的。接下来,就让我们来具体介绍一下如何使用镜像实例化出具体可运行的容器,并对这些容器进行管理。
容器管理
正如我们之前所说,容器是镜像在运行时的实例化。正如基于虚拟机模板可以启动多台虚拟机一样,我们也同样可以基于同一个镜像上启动一个或多个容器。在 Docker 中,启动容器的简便方式是使用docker container run [参数] <镜像名> [指定应用]
命令。在这里,我们在该命令中使用一下参数:
-i
:该参数用于告知该命令以“交互模式”运行容器。-t
:该参数用于告知该命令在容器启动后会进入其命令行终端程序。--name
:该参数用于为创建的容器设置名称。-v
:该参数用于设置容器与其宿主机之间的目录映射关系,它后面通常会紧跟着两个目录参数,第一个是宿主机上的目录,第二个则是映射到容器中目录。另外,我们可以在同一命令中使用多个-v
参数设置多个目录映射。-d
:该参数用于告知该命令创建一个守护式容器在后台运行,这样创建容器后就不会自动登录容器,如果只加-i -t 两个参数,创建后就会自动进去容器。-p
:该参数用于设置容器与其宿主机之间的端口映射,它后面通常会紧跟着两个端口号参数,第一个设置的是宿主机的端口,第二个设置的是在容器内的映射端口。另外,我们可以在同一命令中使用多个-p
参数设置多个端口映射。-e
:该参数用于为容器设置环境变量。--network=host
:该参数用于告知该命令将主机的网络环境映射到容器中,容器的网络与主机相同。
例如,我们可以接下来可以通过docker container run -it --name=myhost ubuntu /bin/bash
这个命令来使用 Ubuntu 镜像实例化并以交互模式启动一个名为myhost
的容器,该容器在启动之后会自动进入其 Bash Shell 终端中,在完成相关操作后,可以执行exit
命令退出,该容器也随之停止。
再例如,我们也可以通过docker container run -dit --name=myhost2 ubuntu
命令来创建一个守护式容器。这类容器在创建时不会立即进入到容器中,并且在容器内执行exit
命令时,容器本身也不会终止运行。如果对于一个需要长期运行的容器来说,我们可以创建一个守护式容器。
对于已在运行的容器,我们可以通过docker container exec -it <容器名或容器ID> [指定应用]
命令进入到该容器中进行相关操作,例如,如果我们想进入之前创建的守护式容器,就可以执行例如:docker container exec -it myhost2 /bin/bash
命令。如果读者不知道当前宿主机中运行了哪一些容器,也可以通过执行docker container ls
命令来进行查看。甚至,如果我们还想在其返回的容器列表中包含已经终止运行的容器,还可以在该命令后面加上--all
或-a
参数,像这样:
$ docker container ls --allCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES1b51ccc03b21 ubuntu "/bin/bash" 43 minutes ago Exited (130) 40 minutes ago myhost
对于上述列表中列出的容器,我们既可以执行docker container stop <容器名或容器ID>
命令停止一个已经在运行的容器,也可以执行docker container start <容器名或容器ID>
命令启动一个已经停止的容器。甚至还可以执行docker container kill <容器名或容器ID>
命令杀掉一个已经在运行的容器。最后,如果确定某个容器不再被使用了,我们也可以通过docker container rm <容器名或容器ID>
命令来删除它。
除此之外,如果我们想将容器的某个运行状态保存下来,以便日后使用,也可以通过执行docker container commit <容器名或容器ID> <镜像名>
命令将容器重新保存为新的镜像。如果希望将这些镜像传递给别人使用,我们还通过docker image save -o <文件名> <镜像名>
命令现有的某个镜像打包成文件,然后别人在收到该文件之后,就可以通过执行docker image load -i <文件名>
命令将该镜像加载到本地。
容器化部署实践
在掌握了 Docker 镜像与容器的基本操作之后,我们就可以来具体地来介绍如何使用 Docker 容器来部署应用程序了。我们会以部署一个最简单的 Express.js 项目开始入手,以便让读者先从整体上初步熟悉一下容器化部署的工作流程,并理解它与传统部署方式的不同。
基本工作流程
下面,就让我们以SSH的方式远程登录到配置了Docker环境的服务器上,并执行以下步骤来部署项目吧。
先通过执行
docker image pull node:17.5.0
命令从 Docker Hub 中拉取一个与我们开发环境相匹配的 Node.js 镜像。如果一切顺利,待拉取操作完成之后,我们就可以在docker image ls
命令返回的本地镜像列表中看到这个版本标签为17.5.0
的 Node.js 镜像了。$ docker image ls REPOSITORY TAG IMAGE ID CREATED SIZEnode 17.5.0 f8c8d04432c3 4 months ago 994MBubuntu latest 825d55fb6340 6 days ago 72.8MBhello-world latest feb5d9fea6a5 6 months ago 13.3kB
接下来,我们要基于该 Node.js 镜像创建一个用于部署
HelloExpress
应用程序的镜像,具体操作是,先进入应用程序源码目录中(这里假设是一个名为HelloExpress
的目录),并创建一个名为Dockerfile
的镜像定义文件,然后在其中写入如下内容。# 声明当前镜像的基础镜像FROM node:17.5.0# 在当前镜像所实例化的容器中创建一个目录RUN mkdir -p /home/Service# 将新建的目录设定为容器的工作目录WORKDIR /home/Service# 设置将当前目录拷贝到容器工作目录COPY ./ /home/Service# 安装项目依赖与 PM2 进程管理器RUN npm install pm2 --global \ && npm install# 设置应用程序使用的端口EXPOSE 3000# 设置用于启动应用程序的命令CMD pm2 start index.js --no-daemon
在保存上述文件之后,继续在该文件所在的目录下执行
docker image build -t helloapp:1.0.0 .
命令来为运行HelloExpress
应用程序的容器创建一个 Docker 镜像。如果一切顺利,待创建操作完成之后,我们就可以在docker image ls
命令返回的本地镜像列表中看到这个名为helloapp
的镜像了。$ docker image ls REPOSITORY TAG IMAGE ID CREATED SIZEhelloapp 1.0.0 5822ce08a2c9 9 seconds ago 1.03GBnode 17.5.0 f8c8d04432c3 4 months ago 994MBubuntu latest 825d55fb6340 6 days ago 72.8MBhello-world latest feb5d9fea6a5 6 months ago 13.3kB
最后,我们就只需要执行
docker container run -d -p 80:3000 helloapp:1.0.0
命令来实例化这个新建的镜像,并运行用于部署该应用程序的容器了,在该命令中,-d
参数用于将容器设置为后台运行;-p
参数于设置服务器与容器之间的端口映射,在这里,我们将服务器的端口也设置成了3000
,这样就无需再修改上一章中配置的反向代理了。如果上述操作过程一切顺利,我们现在就可以在局域网中使用服务器以外的计算机上使用
http://helloexpress.io
这个域名访问HelloExpress
应用程序了,效果与我们之前在图5-5中看到的完全一致。
在完成了上述步骤之后,我们不仅完成了应用程序的容器化部署,构建了该应用程序的容器镜像。这样一来,如果我们在今后的某一时刻想在升级服务器设备,并重新部署HelloExpress
应用程序,或者将其另行部署到另一个网络中的某台服务器上,就可以选择通过docker image save -o hello_image.tar helloapp:1.0.0
命令将这个新建的helloapp
镜像打包成一个名为hello_image
文件,然后在目标设备上获取到该文件,并通过执行docker image load -i hello_image.tar
命令将该镜像加载到本地,然后将它实例化容器并运行即可。当然,如果读者注册了Docker Hub这样的远程镜像仓库服务,也可以直接执行docker image push helloapp:1.0.0
命令将镜像文件上传到远程仓库中,然后就可以在其他设备上通过docker image pull helloapp:1.0.0
命令来获取该镜像了。
容器化指令简介
在上述工作流程中,运维人员的核心任务就是要实现应用程序的容器化,而完成这一任务的关键就是要能熟练掌握Dockerfile
文件的编写方法。从概念上来说,Dockerfile
是一个由一系列镜像构建指令组成的批处理文件,它的本质就是让我们将部署某一应用程序的步骤以镜像文件的方式固定下来,从而实现应用程序的容器化部署。这种构建容器镜像的方式与我们之前介绍的“先进到某个现有的容器中执行一些手动操作,然后再执行docker container commit <容器名或容器ID> <镜像名>
命令来将该容器保存为镜像文件”的方式相比,显得更为自动化一些。所以,我们在这里有必要重点学习一下如何编写Dockerfile
文件,而学习编写Dockerfile
文件的关键就是要掌握这些镜像构建指令。
首先,作为构建 Docker 镜像的第一步,我们需要先使用FROM <镜像名>
指令来声明一个用于构建当前镜像的基础镜像。在计算机领域中,很少有工作是真正从零开始的,大多数情况都是基于现有工作成果的进一步扩展,例如,Ubuntu、Android 都是基于 Linux 内核开发的发行版,而 Linux 内核则又是参照 UNIX 系统接口的重新实现。另外在使用 Java 这一类面向对象的编程语言实现某一功能时,我们的第一步通常也是在现有的类库中选择一个父类来进行扩展,以避免重复发明轮子。FROM <镜像名>
指令的使用思维也是如此,如果我们将 Docker 中镜像与容器类比成面向对象理论中类与对象的关系,那么当前镜像与其基础镜像之间就可以被理解成子类与父类的关系。
在选择基础镜像的时候,运维人员务必要了解接下来部署在容器中的应用程序。在通常情况下,应用程序所依赖的环境越单纯。基础镜像中已完成的工作就可以越多。例如,如果我们要部署的是HelloExpress
这样的应用程序,那么它只需要一个单纯的 Node,js 运行环境,该环境安装在哪一种 Linux 发行版上并不重要,这时候我们只需要指定一个用于运行 Node.js 环境的容器镜像即可。但如果是要部署“线上简历”这种更为复杂的应用程序,那么除了Node.js运行环境,我们还需要使用APT这样的软件包管理器来安装数据库,这时候选择从一个干净的Ubuntu系统环境开始构建镜像可能是一个更好的选择。
在完成基础镜像的选择之后,我们的第二步就是要使用RUN
指令来配置应用程序的运行环境了。该指令的作用就设置一系列在镜像被实例化成容器时需要执行的 shell 命令,这些命令通常用于安装一些应用程序的依赖项和相关工具。需要注意点是,由于 Docker 镜像文件被定义成了一种分成结构,而Dockerfile
文件中的每一条RUN
指令都会在镜像文件中增加一个新的分层,如果不加节制地使用该指令,可能会造成镜像文件毫无意义地过度膨胀。例如在下面的Dockerfile
文件中:
FROM ubuntuRUN apt install wget -yRUN wget -O redis.tar.gz "http://download.redis.io/releases/redis-5.0.3.tar.gz"RUN tar -xvf redis.tar.gz
以上三条RUN
指令会在镜像文件中构建三个分层,但这是毫无必要的,因此我们通常会简化成一条RUN
指令。
FROM ubuntuRUN apt install wget -y \ && wget -O redis.tar.gz "http://download.redis.io/releases/redis-5.0.3.tar.gz" \ && tar -xvf redis.tar.gz
在某些情况下,我们还需要使用WORKDIR <目录名>
指令为应用程序在容器中指定一个工作目录(该目录必须是提前创建好的),然后使用COPY <源文件路径> <容器内路径>
指令将应用程序的源码文件复制到该工作目录中,例如像我们之前所做的:
# 此处省略若干指令RUN mkdir -p /home/ServiceWORKDIR /home/ServiceCOPY ./ /home/ServiceRUN npm install pm2 --global \ && npm install
请注意,在指定好工作目录之后,后续的RUN
指令执行的 shell 命令就会在该目录下执行。除上述指令外,我们还经常会用到以下指令。
ADD <源文件路径> <容器内路径>
指令:该指令的使用方式与功能和COPY <源文件路径> <容器内路径>
指令基本相同。不同之处只在于:如果被复制的源文件是一个tar
压缩文件,该指令会在复制文件时自动将其解压。CMD
指令:该指令虽然和RUN
指令用于执行shell命令,但它们执行命令的时机点不一样,RUN指令执行在构建容器镜像时,而CMD指令执行在容器启动时。后者通常用于为启动的容器指定默认要运行的程序,程序运行结束,容器本身的运行也就随之结束。需要注意的是,如果Dockerfile
文件中存在多个CMD指令,那么只有最后一条会被真正执行。ENV <环境变量名> <要设置的变量值>
指令:该指令用于在容器内设置环境变量,例如,如果我们想将环境变量NODE_VERSION
的值设置为17.5.0
,那么就可以在Dockerfile
文件中设置一条ENV NODE_VERSION 17.5.0
指令。另外,我们也可以用该指令一次设置多个环境变量,命令格式为:ENV <变量1>=<值1> <变量2>=<值2>...
。VOLUME <路径>
指令:该指令用于定义匿名数据卷。在启动容器时忘记挂载数据卷,会自动挂载到匿名卷。定义数据卷有助于避免重要的数据因容器重启而丢失,并可以在一定程度上避免容器的不断膨胀。同样的,我们也可以用该指令一次设置多个数据卷,命令格式为:VOLUME ["<路径1>", "<路径2>"...]
。EXPOSE <端口号>
指令:如果在容器内运行的应用程序需要该容器向外开放指定的端口号,我们就可以使用该指令来声明要开放的端口号。同样的,我们也可以用该指令一次声明多个端口号,命令格式为:EXPOSE <端口号1> <端口号2>...
。USER <用户名>[:<用户组>]
指令:该指令主要用于指定执行后续shell命令的用户和用户组(前提是,该用户和用户组必须已经存在)。
在编写完Dockerfile
文件并将其保存之后,我们就只需要在该文件所在目录上执行docker image build -t <镜像名>
命令来构建镜像文件即可。在这里,-t
参数用于指定<镜像名>
,该名称中可以包含镜像的版本标签,如果没有特别指定标签,其创建的默认版本标签就是latest
;而
具体在这里就应该是我们执行该命令时所在的当前目录。
基于篇幅方面的考虑,我们在这里记录的只是在使用Docker这一工具容器化 Express.js 应用程序时可能会用到的常用指令。如果读者希望更全面地了解在使用Dockerfile
文件构建 Docker 镜像文件时所有可用的指令,可以自行前往 Docker 的官方网站查看其提供的文档资料。
关键词:
-
阿里一面:MySQL 单表数据最大不要超过多少行?为什么?这样回答满分! 世界热消息
来源:https: my oschina net u 4090830 blog 5559454 1背景作为在后端圈开车的多年老司机,是不是经常
来源: Docker 学习笔记
过滤器链及责任链设计模式 观热点
阿里一面:MySQL 单表数据最大不要超过多少行?为什么?这样回答满分! 世界热消息
gps网络时间服务器(时间同步装置)助力电力信息化建设|世界观焦点
deepin-for-arm64支持
全球关注:债市观察:利好钝化收益率窄幅波动 十债2.7%踌躇踏步
【世界新视野】抖音打不开视频怎么回事_抖音打不开
当前要闻:山姆同款蛋糕杭州卖165上海卖95 网友直呼太坑:你遇到过吗
绵云般口感!和路雪千层雪冰淇淋3.5元官方大促(商超6元)
今年唯一LCD性能机!Redmi Note 12T Pro外观公布-当前独家
重点聚焦!研究称每天排便超一次或影响健康:心力衰竭风险增加33%
全球快资讯丨接近小米1英寸了!曝iPhone 16 Pro Max主摄大升级
“候鸟”老人现在是否可以在海南澄迈县买房子?深蓝苑·滨江城PK五指山森林湖养老分析!|焦点简讯
JS中的arguments
今日视点:关于切片参数传递的问题
天天视点!十大券商看后市:A股底部特征浮现 市场进一步下行空间较小
神舟十六号30日9时31分发射:三名航天员名单公布 首次有大学教授
华为畅享60X根治续航焦虑!充电宝完全派不上用场了
全球微资讯!华为智选车终于出轿车 奇瑞EH3谍照曝光:华为ADS摄像头瞩目
世界今热点:小屏4K“天花板”优派推出新款23.8英寸显示器 1999元
神十六乘组公布:载荷专家将首登天宫-动态
搜狐汽车全球快讯 | 比亚迪或考虑在法国建厂 比亚迪:正评估建厂可行性-全球资讯
【独家】Linux工作原理2常用基本命令和目录层次结构
618开车神价 爱国者4TB PCIe 4.0硬盘999元(国产长寿TLC闪存)-全球今亮点
特斯拉、丰田、BBA等都靠边!离开中国:世界无法生产电动车电池 当前观点
苹果iPhone销冠地位稳如泰山 今年618攻略请收好
热消息:央视网评《家有儿女》疑似被恶意评分:小心网络评分变“粉黑大战”
山东舰穿越台湾海峡北上,台军兵推双航母“攻台”_今日热讯
【读财报】三年期互联网主题基金透视:天弘基金业绩垫底 长盛基金风格较激进
天天快消息!内存频率、容量继续狂飙 科赋宣布DDR5-8600:单条48GB
环球今亮点!史上最大屏!iPhone 16 Pro系列确认6.3/6.9英寸屏幕
LCD党福音!Redmi Note 12T Pro跑分出炉:搭载天玑8200-Ultra
每日资讯:101岁杨振宁在西湖大学致辞 给自己打分还不错 张朝阳赞其超越霍金
网络授课用什么软件好 网络授课用什么软件
【世界独家】读数据压缩入门笔记01_数据压缩导读
苹果iOS 17超前瞻 近年来最没存在感的一代?
印度官员为找手机抽掉水库210万升水!最后工作没了|当前时讯
天天热议:iPhone 15即将量产 富士康重金招人:每人3500元奖金
焦点热议:百元股上新 双元科技申购
CISCN_Dozer战队wp
环球新资讯:AutoCAD所有版本总结、序列号密钥总结大全(转载)
ZooKeeper论文阅读笔记 焦点关注
滚筒洗衣机真的比波轮的更好吗?一文读懂
死亡风险直降12%!爱发朋友圈 竟然更长寿 环球观焦点
猫咪为什么要花那么多时间梳理毛发?只是臭美吗?_速看
世界短讯!我父亲配享太庙是谁的台词
重庆长城宽带价格表_重庆长城宽带
银行汇票的付款人是谁_银行本票的有效期限为1个月 而其提示付款期限为自出票日起最长不
王老吉百家姓氏罐大促:12罐到手价29.9元
祭祀的拼音_祭祀怎么读
Oracle 死锁与慢查询总结
当前关注:天津聚力产业链优势打造信创产业基地
全球今头条!丫丫启程回京:将返回北京动物园
当前热议!Unity的Undo:详解解析与实用案例
「学习笔记」(扩展)中国剩余定理
2023-05-28:为什么Redis单线程模型效率也能那么高?|通讯
9)子查询
女模特为长高做手术“打断”腿两次:163变180 如踩高跷
网购虾条居然收到了差评返现卡:主打的就是真诚_热点
热头条丨女生一条微信状态让初中班主任找到:他像太阳一样照耀着大家
资讯推荐:猫眼端午档观众最想看的电影!《消失的她》即将上映:倪妮主演
当前热门:世乒赛战报:陈梦/王艺迪、王楚钦/樊振东夺冠,中国包揽5冠
Doris(三) -- 索引_世界快看点
梅西、迪玛利亚领衔!阿根廷国家队中国行名单公布|关注
南京玄武湖隧道内一辆宝马5系突发自燃 浓烟蔓延数百米_时快讯
哦?上一轮G7塔图姆刚刚刷新历史记录轰了51分! 天天新视野
6年了!iPhone 15无线充电迎来升级:支持Qi2标准 速讯
安卓性能最强旗舰!iQOO Neo8 Pro下周开卖:3099元起 新要闻
马云励志人生正能量八句话_马云的八句经典语录
天天关注:系统设计:从零用户扩展到百万用户
观热点:想买洗地机的用户必看:一文让你读懂洗地机
小米Civi 3上16+1TB大存储!雷军:中端机这么猛|环球简讯
《家有儿女》疑似被恶意打分:网络评分还可信吗?
“靠天吃饭”的水电如何“旱涝保收”?清洁能源助力迎峰度夏
大模型全情投入,低代码也越来越清晰
游泳时 千万不要穿白色和蓝色的泳衣 真的很危险! 世界播报
自营牧场:辉山纯牛奶7.6元/L大促 速囤-当前头条
C919飞机上有五福临门主题餐:内含上海特产大白兔牛奶
腾讯又一手游没了!《街头篮球》国服宣布7月停服
子午觉是睡眠养生之法 子午觉其中的子时和午时分别指的是-全球微速讯
世界观点:时隔6年姗姗来迟!《极限竞速》新作封面图终于公布
格局打开!波音官方庆祝C919首航成功
结转销售成本会计分录怎么写_结转销售成本会计分录
抱紧华为大腿一年 赛力斯挤进新势力前三:但还未跨过生死线|天天新要闻
世界今日报丨《西游ABC》IGN 8分:吴彦祖演技精湛 神话故事迷人
10万级大五座纯电轿车 宝骏云朵信息公开:无模组电池放心跑_全球热消息
深入分析:近端梯度下降法、交替方向乘子法、牛顿法
快看点丨30年老律师用ChatGPT旁征博引:结果被骗得禁止执业!
WinXP问世22年后 黑客发布离线激活算号器:强大程度被低估了
见证历史!国产大飞机C919首航顺利降落
西藏拉萨:藏式克朗球协会正式成立
MAC中文版 Final Cut Pro X(FCPX) V10.6.6 专属视频剪辑后期工具安装教程_全球快看
微软宣布Windows全球用户超10亿!Win11是史上最可靠操作系统 快看
特斯拉陶琳:谣言层出不穷、原因之一是流量至上
中国经济信心说丨人口高质量发展 从直面每个家庭的实际困难开始
环球微动态丨Python 标准类库-因特网数据处理之Base64数据编码
世界微资讯!2023 Sun Simiao TCM International Forum kicks off in China's Shaanxi
注意防范 7省市将现大到暴雨:部分地区有雷暴大风或冰雹-环球关注
快资讯丨直播:国产大飞机C919商业首飞!网友感叹中国人终于要坐上国产大飞机
时隔10年 索尼全新PS掌机Q公布!电池续航很堪忧