Docker compose 和共享数据

Docker compose 和共享数据

容器

如何进入容器

运行容器

docker run=docker create + docker start

docker run 是启动容器的方法。在讨论 Dockerfile 时我们已经学习到,可用三种方式指定容器启动时

执行的命令:

  1. CMD 指令。

  2. ENTRYPOINT 指令。

  3. 在 docker run 命令行中指定。

例如下面的例子:

bash 复制代码
[root@docker ~]# docker create ubuntu #使用ubuntu镜像创建容器
eb1aa0ca86b2d49250ebe64913af50e88482ad68b9c3e61ef8f8da9c24b00f7a #新创建的容器长ID
[root@docker ~]# docker ps -a #create的容器状态时Created
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
eb1aa0ca86b2 ubuntu "/bin/bash" 2 seconds ago `Created`
quizzical_goldwasser
[root@docker ~]# docker start eb1aa0ca86b2 #启动容器, 刚创建的容器ID
eb1aa0ca86b2
[root@docker ~]# docker ps -a #查看容器状态, 启动了又退出了
CONTAINER ID IMAGE COMMAND CREATED STATUS
PORTS NAMES
eb1aa0ca86b2 ubuntu "/bin/bash" 23 seconds ago Exited (0) 3 seconds ago
quizzical_goldwasser
bash 复制代码
[root@docker ~]# docker run ubuntu pwd #使用ubuntu镜像创建容器并执行pwd命令
/ [
root@docker ~]#

容器启动时执行 pwd ,返回的 / 是容器中的当前目录。 执行 docker ps 或 docker container ls可以查看 Docker host 中当前运行的容器:

bash 复制代码
[root@docker ~]# docker ps #查看所有正在运行中的容器
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES

-a 会显示所有状态的容器,可以看到,之前的容器已经退出了,状态为 Exited 。

让容器长期运行

因为容器的生命周期依赖于启动时执行的命令,只要该命令不结束,容器也就不会退出。

理解了这个原理,我们就可以通过执行一个长期运行的命令来保持容器的运行状态。例如执行下面的命令:

bash 复制代码
[root@docker ~]# docker run ubuntu /bin/bash -c "while true ; do sleep 1 ; echo
hahaha; done"

while 语句让 bash 不会退出。我们可以打开另一个终端查看容器的状态:

可见容器仍处于运行状态。不过这种方法有个缺点:它占用了一个终端。

我们可以加上参数 -d 以后台方式启动容器。

bash 复制代码
[root@docker ~]# docker run -d ubuntu /bin/bash -c "while true ; do sleep 1 ;
echo hahaha; done"
2a0bfa267fe146753b4fc8b23d55b08fbe3a5f9b5e093de6885133f6bbd20c56
[root@docker ~]#

容器启动后回到了 docker host 的终端。这里看到 docker 返回了一串字符,这是容器的 ID。通过docker ps 查看容器:

两种进入容器的方法

我们经常需要进到容器里去做一些工作,比如查看日志、调试、启动其他进程等。有两种方法进入容器: attach 和 exec。

docker attach

通过 docker attach 可以 attach 到容器启动命令的终端,例如:

bash 复制代码
[root@docker ~]# docker run -d ubuntu /bin/bash -c "while true ; do sleep 1 ;
echo I_am_in_container ; done"
dc508b94447f83b46080267580607569a187fcc7f780433f646e9d66949731a6
[root@docker ~]#
[root@docker ~]# docker attach
dc508b94447f83b46080267580607569a187fcc7f780433f646e9d66949731a6
I_am_in_container
I_am_in_container
I_am_in_container
I_am_in_container

注:可通过 Ctrl+p 然后 Ctrl+q 组合键退出 attach 终端。

docker exec

通过 docker exec 进入相同的容器:

说明如下:

① -it 以交互模式打开 pseudo-TTY,执行 bash,其结果就是打开了一个 bash 终端。

② 进入到容器中,容器的 hostname 就是其 "短ID"。

③ 可以像在普通 Linux 中一样执行命令。 ps -elf 显示了容器启动进程 while 以及当前的 bash 进程。

④ 执行 exit 退出容器,回到 docker host。

docker exec -it bash|sh 是执行 exec 最常用的方式。

attach VS exec

attach 与 exec 主要区别如下:

  1. attach 直接进入容器 启动命令 的终端,不会启动新的进程。
  2. exec 则是在容器中打开新的终端,并且可以启动新的进程。
  3. 如果想直接在终端中查看启动命令的输出,用 attach;其他情况使用 exec。

当然,如果只是为了查看启动命令的输出,可以使用 docker logs 命令:

bash 复制代码
[root@docker ~]# docker logs -f dc508b94447f
I_am_in_container
I_am_in_container
I_am_in_container
I_am_in_container
I_am_in_container
I_am_in_container
I_am_in_container

一张图搞懂容器所有操作

export和import容器

export-容器导出

将容器导出为一个tar包

bash 复制代码
[root@docker ~]# docker export --help
Usage: docker export [OPTIONS] CONTAINER
Export a container's filesystem as a tar archive
Aliases:
docker container export, docker export
Options:
-o, --output string Write to a file, instead of STDOUT

不管此时这个容器是否处于运行状态,都可以导出为文件。

示例:

bash 复制代码
# 创建容器httpd1用于测试
[root@docker ~]# docker run -d --name httpd1 httpd
e4f0a329c4df50ef0afb0bf21e22edc20e5a24c03ae64c6340aa2992d6e32525
[root@docker ~]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS
PORTS NAMES
e4f0a329c4df httpd "httpd-foreground" 3 minutes ago Up 3 minutes
80/tcp httpd1
[root@docker ~]# docker export httpd1 -o myhttpd.tar
[root@docker ~]# ls
myhttpd.tar
import-容器tar包导入

import将export导出的tar包导入成为镜像

bash 复制代码
[root@docker ~]# docker import myhttpd.tar
sha256:9ae4699fa217a7240c73a50e93a8c1d359d22cf60869d350c8aab8b3c02aabcf
[root@docker ~]# docker import myhttpd.tar myweb:v1
sha256:9fcb90561d2014130ef19f9dd8af5efd7a0a4a2154907bc818ac6b887f13755c
[root@docker ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
myweb v1 9fcb90561d20 3 seconds ago 146MB
<none> <none> 9ae4699fa217 32 seconds ago 146MB
httpd latest 90f191b9781e 11 days ago 148MB
hello-world latest 74cc54e27dc4 6 months ago 10.1kB
[root@docker ~]#

实现容器的底层技术

cgroup 和 namespace 是最重要的两种技术。 cgroup 实现资源限额, namespace 实现资源隔离。

cgroup

cgroup 全称 Control Group。 Linux 操作系统通过 cgroup 可以设置进程使用 CPU、内存 和 IO 资源的限额。相信你已经猜到了:前面我们看到的 --cpu-shares 、 -m 、 --device-write-bps 实际上就是在配置 cgroup。

bash 复制代码
[root@docker docker]# docker run -it --cpu-shares 512 ubuntu-with-stress -c 1 -v
stress: info: [1] dispatching hogs: 1 cpu, 0 io, 0 vm, 0 hdd
stress: dbug: [1] using backoff sleep of 3000us
stress: dbug: [1] --> hogcpu worker 1 [7] forked
namespace

Linux 实现这种方式的技术是 namespace。 namespace 管理着 host 中全局唯一的资源,并可以让每个容器都觉得只有自己在使用它。换句话说, namespace 实现了容器间资源的隔离。

Linux 使用了六种 namespace,分别对应六种资源: Mount、 UTS、 IPC、 PID、 Network 和 User,下面我们分别讨论。

Mount namespace

Mount namespace 让容器看上去拥有整个文件系统。

UTS namespace

简单的说, UTS namespace 让容器有自己的 hostname。 默认情况下,容器的 hostname 是它的短ID,可以通过 -h 或 --hostname 参数设置。

IPC namespace

IPC namespace 让容器拥有自己的共享内存和信号量(semaphore)来实现进程间通信,而不会与host 和其他容器的 IPC 混在一起。

PID namespace

我们前面提到过,容器在 host 中以进程的形式运行。例如当前 host 中运行了两个容器:

bash 复制代码
[root@docker ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS
PORTS NAMES
a80f1f8b692c ubuntu "/bin/bash" 26 seconds ago Up 4 seconds
interesting_davinci
835dd4eebda2 httpd "httpd-foreground" 56 seconds ago Up 55 seconds
80/tcp adoring_pare

通过 ps axf 可以查看容器进程:

所有容器的进程都挂在 dockerd 进程下,同时也可以看到容器自己的子进程。 如果我们进入到某个容器, ps 就只能看到自己的进程了:

bash 复制代码
[root@docker ~]# docker exec -it a80f1f8b692c bash
root@a80f1f8b692c:/#
root@a80f1f8b692c:/# ps axf
PID TTY STAT TIME COMMAND
17 pts/1 Ss 0:00 bash
25 pts/1 R+ 0:00 \_ ps axf
1 pts/0 Ss+ 0:00 /bin/bash
Network namespace

Network namespace 让容器拥有自己独立的网卡、 IP、路由等资源。我们会在后面网络章节详细讨论。

User namespace

User namespace 让容器能够管理自己的用户, host 不能看到容器中创建的用户。

bash 复制代码
[root@docker ~]# docker exec -it a80f1f8b692c bash
root@a80f1f8b692c:/#
root@a80f1f8b692c:/# useradd gaoqd #容器中创建用户gaoqd
root@a80f1f8b692c:/#
root@a80f1f8b692c:/# exit
exit
[root@docker ~]# su - gaoqd #宿主机中并没有用户gaoqd
su: user gaoqd does not exist

如何共享数据

容器与 host 共享数据

我们有两种类型的 data volume,它们均可实现在容器与 host 之间共享数据,但方式有所区别。

对于 bind mount 是非常明确的:直接将要共享的目录 mount 到容器。具体请参考前面 httpd 的例子,不再赘述。

docker managed volume 就要麻烦点。由于 volume 位于 host 中的目录,是在容器启动时才生成,所以需要将共享数据拷贝到 volume 中。请看下面的例子:

bash 复制代码
[root@docker ~]# docker run -d -p 80:80 -v /usr/local/apache2/htdocs httpd
889b9d5309bd12499f8e4ee78a491d9ba7acef5660be00f30cda6569c9974c6f
[root@docker ~]#
[root@docker ~]# curl 127.0.0.1:80
<html><body><h1>It works!</h1></body></html>
[root@docker ~]#
[root@docker ~]# docker cp ~/htdocs/index.html
889b9d5309bd:/usr/local/apache2/htdocs 
#将host os的/root/htdocs/index.html拷贝到容器中的/usr/local/apache2/htdocs目录下
Successfully copied 2.05kB to 889b9d5309bd:/usr/local/apache2/htdocs
[root@docker ~]#
[root@docker ~]# curl 127.0.0.1:80
updated index page!
[root@docker ~]#
容器之间共享数据

第一种方法是将共享数据放在 bind mount 中,然后将其 mount 到多个容器。还是以 httpd 为例,不过这次的场景复杂些,我们要创建由三个 httpd 容器组成的 web server 集群,它们使用相同的 html 文件,操作如下:

  1. 将 $HOME/htdocs mount 到三个 httpd 容器。
bash 复制代码
[root@docker ~]# docker run --name web1 -d -p 80 -v
~/htdocs:/usr/local/apache2/htdocs httpd
994e75ef90a80365d7ea7733c727c3b1d24e5423d59a74185304462b7137d774
[root@docker ~]#
[root@docker ~]# docker run --name web2 -d -p 80 -v
~/htdocs:/usr/local/apache2/htdocs httpd
28ff7c72d18b30123ff7e383db211890a0007e48817cdc73e7f50e9f2233eee6
[root@docker ~]#
[root@docker ~]# docker run --name web3 -d -p 80 -v
~/htdocs:/usr/local/apache2/htdocs httpd
160d823d985bad0ce7c27a0d301956f3c7ab4e9ca34c5be2601b3374fa30f6f1
[root@docker ~]#
  1. 查看当前主页内容。
bash 复制代码
[root@docker ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS
PORTS NAMES
160d823d985b httpd "httpd-foreground" 13 seconds ago Up 12 seconds
0.0.0.0:32770->80/tcp, :::32770->80/tcp web3
28ff7c72d18b httpd "httpd-foreground" 19 seconds ago Up 18 seconds
0.0.0.0:32769->80/tcp, :::32769->80/tcp web2
994e75ef90a8 httpd "httpd-foreground" 29 seconds ago Up 27 seconds
0.0.0.0:32768->80/tcp, :::32768->80/tcp web1
[root@docker ~]#
[root@docker ~]# curl 127.0.0.1:32770 #这个32770端口是host os映
射到web3的duan'k
updated index page!
[root@docker ~]# curl 127.0.0.1:32769
updated index page!
[root@docker ~]# curl 127.0.0.1:32768
updated index page!
[root@docker ~]#
  1. 修改 volume 中的主页文件,再次查看并确认所有容器都使用了新的主页。
bash 复制代码
[root@docker ~]# echo "This is a new index page for web cluster" >
~/htdocs/index.html
[root@docker ~]#
[root@docker ~]# curl 127.0.0.1:32770
This is a new index page for web cluster
[root@docker ~]# curl 127.0.0.1:32769
This is a new index page for web cluster
[root@docker ~]# curl 127.0.0.1:32768
This is a new index page for web cluster
[root@docker ~]#

Docker-compose

我们知道使用一个 Dockerfile 模板文件,可以让用户很方便的定义一个单独的应用容器。然而,在日常工作中,经常会碰到需要多个容器相互配合来完成某项任务的情况。例如要实现一个 Web 项目,除了Web 服务容器本身,往往还需要再加上后端的数据库服务容器,甚至还包括负载均衡容器等。如果每个容器都要按顺序手动启停,那么维护工作量将会很大,而且工作效率也很低。

Docker Compose 可以轻松、高效地管理容器,它是一个用于定义和运行多容器的管理工具。

它通过一个单独的 docker-compose.yml 模板文件(YAML 格式)定义一组相关联资源集。

Compose 中有两个重要的概念:

  • 服务 ( service ):一个应用的容器,实际上可以包括若干运行相同镜像的容器实例。
  • 项目 ( project ):由一组关联的应用容器组成的一个完整业务单元,在 docker-compose.yml 文件中定义。

version

bash 复制代码
[root@docker ~]# docker compose version
Docker Compose version v2.27.0
Compose 模板

模板文件是使用 Compose 的核心,涉及到的指令关键字也比较多。但大家不用担心,这里面大部分指令跟 docker run 相关参数的含义都是类似的。

默认的模板文件名称为 docker-compose.yml ,格式为 YAML 格式。

实战-Wordpress
bash 复制代码
[root@docker ~]# docker run -tid --name db --restart always -v /db:/var/lib/mysql
-e MYSQL_ROOT_PASSWORD=huawei -e MYSQL_DATABASE=wordpress mysql
d3fa1d9e6ecc285427b38bca95130cade90ca69ee9487825db92b3bc8af4c995
[root@docker ~]# docker run -tid --name blog -v /web:/var/www/html -p 80:80 --
link db -e WORDPRESS_DB_HOST=db -e WORDPRESS_DB_USER=root -e
WORDPRESS_DB_PASSWORD=huawei -e WORDPRESS_DB_NAME=wordpress wordpress
b5b80bd69f57bff576415f2e449a23fe6cb37be353f9b4b69d24d60a2fb9a6e1
[root@docker ~]#
[root@docker ~]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS
PORTS NAMES
b5b80bd69f57 wordpress "docker-entrypoint.s..." 13 seconds ago Up 12
seconds 0.0.0.0:80->80/tcp, :::80->80/tcp blog
d3fa1d9e6ecc mysql "docker-entrypoint.s..." 26 seconds ago Up 25
seconds 3306/tcp, 33060/tcp db
MYSQL_DATABASE=wordpress

测试效果:

通过docker compose来统一管理这两个容器呢?

假设新建一个名为 wordpress 的文件夹,然后进入这个文件夹,创建 docker-compose.yml 文件

bash 复制代码
# 删除之前的环境
[root@docker ~]# docker rm -f $(docker ps -aq)
# 通过docker compose实现多个容器一起启动
[root@docker ~]# mkdir wordpress
[root@docker ~]# cd wordpress/
[root@docker wordpress]# vim docker-compose.yml
services:
blog: #服务名字, 相当于docker run的时候指定的一个名
称
image: wordpress:latest #必选, 镜像的名字
restart: always
links:
- db
ports: #可选, 等价于 docker run 里的 -p 选项指定端口映
射
- "80:80"
environment: #可选, 等价于 docker run 里的 --env 选项设置环境变量
- WORDPRESS_DB_HOST=db
- WORDPRESS_DB_USER=root
- WORDPRESS_DB_PASSWORD=huawei
- WORDPRESS_DB_NAME=wordpress
db:
image: mysql:latest
restart: always
environment:
- MYSQL_ROOT_PASSWORD=huawei
- MYSQL_DATABASE=wordpress
[root@docker wordpress]# docker compose config -q #检测语法

后端运行

bash 复制代码
[root@docker wordpress]# docker compose up -d
[+] Running 3/3
✔ Network wordpress_default Created 0.0s
✔ Container wordpress-db-1 Started 0.3s
✔ Container wordpress-blog-1 Started 0.6s

查看现象

bash 复制代码
[root@docker wordpress]# docker ps
CONTAINER ID IMAGE COMMAND CREATED
STATUS PORTS NAMES
85b438b2ff89 wordpress:latest "docker-entrypoint.s..." About a minute ago
Up About a minute 0.0.0.0:80->80/tcp, :::80->80/tcp wordpress-blog-1
3c340ed1e299 mysql:latest "docker-entrypoint.s..." About a minute ago
Up About a minute 3306/tcp, 33060/tcp wordpress-db-1

d 0.0s

✔ Container wordpress-db-1 Started 0.3s

✔ Container wordpress-blog-1 Started 0.6s

复制代码
查看现象  

```bash
[root@docker wordpress]# docker ps
CONTAINER ID IMAGE COMMAND CREATED
STATUS PORTS NAMES
85b438b2ff89 wordpress:latest "docker-entrypoint.s..." About a minute ago
Up About a minute 0.0.0.0:80->80/tcp, :::80->80/tcp wordpress-blog-1
3c340ed1e299 mysql:latest "docker-entrypoint.s..." About a minute ago
Up About a minute 3306/tcp, 33060/tcp wordpress-db-1
相关推荐
袁小皮皮不皮2 小时前
HCIP-BFD 学习笔记
运维·服务器·网络·笔记·网络协议·学习·智能路由器
恋奴娇2 小时前
ubuntu 25 gnome-screenshot 录屏启动失败 原因pipewire服务未启动
linux·运维·ubuntu
Zhu7582 小时前
[配置管理]k8s集群中airflow的端口转发
云原生·容器·kubernetes
泓博2 小时前
Macbook Docker Compose不识别
运维·docker·容器
susu10830189112 小时前
windows系统的WSL的Ubuntu安装docker
linux·ubuntu·docker
wanhengidc2 小时前
显卡服务器都有哪些功能
运维·服务器·人工智能·科技·智能手机·云计算
老年DBA2 小时前
ZFS存储池配置终极指南
运维·数据库
Riu_Peter3 小时前
【技术】Docker 部署 MySQL
mysql·adb·docker
东北甜妹3 小时前
K8s Helm 和蓝绿发布,金丝雀发布
云原生·容器·kubernetes