docker学习笔记

287 阅读21分钟

1. 虚拟化与容器技术

一个软件可以正确运行,前提是保证操作系统的设置、各种库和组件的安装都必须正确

1.1 虚拟机

虚拟机(virtual machine)是带环境安装的一种解决方案。它可以在一种操作系统里面运行另一种操作系统,典型代表:vmware

一般是通过一个虚拟机监视器 ( Virtual Machine Monitor ) 的设施来隔离操作系统与硬件或者应用程序和操作系统,以此达到虚拟化的目的。这个夹在其中的虚拟机监视器,常常被称为 Hypervisor

image.png

缺点:资源占用多、冗余步骤多、启动速度慢。

1.2 Linux 容器

Linux 发展出了另一种虚拟化技术:Linux 容器。

Linux 容器是对进程进行隔离。对于容器里面的进程来说,它接触到的各种资源都是虚拟的,从而实现与底层系统的隔离。

优点:启动快、资源占用少、体积小。

2. Docker 简介

  • Docker 是目前最流行的 Linux 容器解决方案。属于 Linux 容器的一种封装,提供简易的容器使用接口。

  • Docker 将应用程序及其依赖打包在一个文件里面。运行它会生成一个虚拟容器。程序在这个虚拟容器里运行,就好像在真实的物理机上运行一样。

2.1 Docker应用场景

  • 节省项目环境部署时间
  • 单项目打包
  • 整套项目打包
  • 新开源技术
  • 环境一致性
  • 持续集成
  • 微服务
  • 弹性伸缩

3. Docker和KVM 对比

image.png

  • 没有了 虚拟操作系统虚拟机监视器 这两个层次,大幅减少了应用程序运行带来的额外消耗。

4. Docker 体系结构

  • containerd 是一个守护进程,使用runc管理容器,向Docker Engine提供接口
  • shim 只负责管理一个容器
  • runC是一个轻量级工具,只用来运行容器

image.png

5. Docker内部组件

  • namespaces 命名空间,Linux内核提供的一种对进程资源隔离的机制,例如进程、网络、挂载等资源
  • cgroups 控制组,linux内核提供的一种限制进程资源的机制,例如cpu 内存等资源
  • unonFS 联合文件系统,支持将不同位置的目录挂载到同一虚拟文件系统,形成一种分层的模型

6. docker安装

  • 主要分为 社区版 (CE) --- 免费 ; 企业版 (EE) --- 收费
  • 笔者在centos7中安装
sudo yum install yum-utils device-mapper-persistent-data lvm2

sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
sudo yum install docker-ce

sudo systemctl enable docker
sudo systemctl start docker

7. Docker架构

image.png

8. image镜像

  • Docker 把应用程序及其依赖,打包在 image 文件里面。只有通过这个文件,才能生成 Docker 容器
  • image 文件可以看作是容器的模板
  • Docker 根据 image 文件生成容器的实例
  • 同一个 image 文件,可以生成多个同时运行的容器实例
  • 镜像不是一个单一的文件,而是有多层
  • 容器其实就是在镜像的最上面加了一层读写层,在运行容器里做的任何文件改动,都会写到这个读写层里。如果容器删除了,最上面的读写层也就删除了,改动也会丢失
  • 我们可以通过docker history <ID/NAME> 查看镜像中各层内容及大小,每层对应着Dockerfile中的一条指令
命令含义语法案例
pull拉取镜像docker pull [imageName]
push推送一个镜像到镜像仓库docker push [imageName]
rmi删除镜像docker rmi [imageName]
images查看全部镜像docker image ls
search查找镜像docker search [imageName]
history查看镜像历史docker history [imageName]
inspect显示一个或多个镜像详细信息docker inspect [imageName]
prune移除未使用的镜像,没有标记或补任何容器引用docker image prunedocker image prune
tag标记本地镜像,将其归入某一仓库docker tag [OPTIONS] IMAGE[:TAG] [REGISTRYHOST/][USERNAME/]NAME[:TAG]docker tag centos:7 caixukun/centos:v1
export将容器文件系统作为一个tar归档文件导出到STDOUTdocker export [OPTIONS] CONTAINERdocker export -o hello-world.tar bce17c1088ad
import导入容器快照文件系统tar归档文件并创建镜像docker import [OPTIONS] file/URL/- [REPOSITORY[:TAG]]docker import hello-world.tar
save将指定镜像保存成tar文件docker save [OPTIONS] IMAGE [IMAGE...]docker save -o hello-world.tar hello-world:latest
load加载tar文件并创建镜像docker load -i hello-world.tar
build根据Dockerfile构建镜像docker build [OPTIONS] PATH / URL / -docker build -t caixukun/ubuntu:v1 .
  • 用户既可以使用 docker load 来导入镜像存储文件到本地镜像库,也可以使用 docker import 来导入一个容器快照到本地镜像库
  • 这两者的区别在于容器(import)快照文件将丢弃所有的历史记录和元数据信息(即仅保存容器当时的快照状态),而镜像(load)存储文件将保存完整记录,体积也会更大

8.1 查看镜像

docker image ls
字段含义
REPOSITORY仓库地址
TAG标签
IMAGE_ID镜像ID
CREATED创建时间
SIZE镜像大小

8.2 查找镜像

docker search ubuntu
字段含义
NAME名称
DESCRIPTION描述
STARTS星星的数量
OFFICIAL是否官方源

8.3 拉取镜像

docker  pull docker.io/hello-world
  • docker image pull是抓取 image 文件的命令
  • docker.io/hello-world 是 image 文件在仓库里面的位置,其中docker.io是 image的作者,hello-world 是 image 文件的名字
  • Docker 官方提供的 image 文件,都放在docker.io组里面,所以它的是默认组,可以省略 docker image pull hello-world

8.4 删除镜像

docker rmi  hello-world

9. 容器

  • docker run 命令会从 image 文件,生成一个正在运行的容器实例。

  • docker container run命令具有自动抓取 image 文件的功能。如果发现本地没有指定的 image 文件,就会从仓库自动抓取

  • 输出提示以后,hello world就会停止运行,容器自动终止。但有些容器不会自动终止(例如nginx)

  • image 文件生成的容器实例,本身也是一个文件,称为容器文件

  • 关闭容器并不会删除容器文件,只是容器停止运行

命令含义案例
run从镜像运行一个容器docker run ubuntu /bin/echo 'hello-world'
ls列出容器docker container ls
inspect显示一个或多个容器详细信息docker inspect
attach连接正在运行的容器,可以同时连接上同一个container来共享屏幕docker attach 6d1a25f95132
stats显示容器资源使用统计docker container stats
top显示一个容器运行的进程docker container top
update更新一个或多个容器配置docker update -m 500m --memory-swap -1 6d1a25f95132
port列出指定的容器的端口映射docker container port containerID
ps查看当前运行的容器docker ps -a -l
kill终止容器docker kill [containerId]
rm删除容器docker rm [containerId]
start启动已经生成、已经停止运行的容器文件docker start [containerId]
stop终止容器运行docker stop [containerId] docker container stop $(docker container ps -aq)
logs查看 docker 容器的输出docker logs [containerId]
exec进入一个正在运行的 docker 容器执行命令docker container exec -it f6a53629488b /bin/bash
cp从正在运行的 Docker 容器里面,将文件拷贝到本机docker container cp f6a53629488b:/root/root.txt .
commit根据一个现有容器创建一个新的镜像docker commit -a "caixukun" -m "mynginx" a404c6c174a2 mynginx:v1

docker容器的主线程(dockfile中CMD执行的命令)结束,容器会退出

9.1 启动容器

docker run ubuntu /bin/echo "Hello world"
  • docker: Docker 的二进制执行文件。
  • run: 与前面的 docker 组合来运行一个容器。
  • ubuntu指定要运行的镜像,Docker首先从本地主机上查找镜像是否存在,如果不存在,Docker 就会从镜像仓库 Docker Hub 下载公共镜像。
  • /bin/echo "Hello world": 在启动的容器里执行的命令

Docker以ubuntu镜像创建一个新容器,然后在容器里执行 bin/echo "Hello world",然后输出结果

参数含义
-i --interactive交互式
-t --tty分配一个伪终端
-d --detach运行容器到后台
-a --attach list附加到运行的容器
-e --env list设置环境变量
-p --publish list发布容器端口到主机
-P --publish-all发布所有端口到主机

9.2. 查看容器

docker ps

展示的字段含义如下:

字段含义
CONTAINER ID容器ID
IMAGE使用的镜像
COMMAND使用的命令
CREATED创建时间
STATUS状态
PORTS端口号
NAMES自动分配的名称

9.3 运行交互式的容器

docker run -i -t ubuntu /bin/bash
  • -t=--interactive 在新容器内指定一个伪终端或终端。
  • -i=--tty 允许你对容器内的标准输入 (STDIN) 进行交互。
  • 可以通过运行exit命令或者使用CTRL+D来退出容器。

9.4 往后台运行容器

docker run --detach centos ping [www.baidu.com](http://www.baidu.com)
docker ps
docker logs --follow ad04d9acde94
docker stop ad04d9acde94

9.5 kill与stop

docker kill 5a5c3a760f61
docker stop 5a5c3a760f61
  • kill 会强行终止容器运行
  • stop 首先给容器发送一个TERM信号,让容器做一些退出前必须的保护性、安全性操作,然后让容器自动停止运行,如果在一段时间内,容器还是没有停止,再进行kill -9,强行终止

9.6 删除容器

  • docker rm: 删除容器
  • docker rmi: 删除镜像
  • docker rm $(docker ps -a -q): -a列出所有容器,-q 会列出容器id,然后删除所有列出id的容器

9.7 容器其余细节

#启动容器
docker start \[containerId]

#停止容器
docker stop \[containerId]

#进入一个容器
docker attach \[containerID]

#进入一个正在运行中的容器
docker container -exec -it \[containerID] /bin/bash

#拷贝文件
docker container cp \[containerID] [要拷贝的容器目录] [要拷贝到宿主机的目录]

#自动删除
docker run --rm ubuntu /bin/bash

10. commit制作个性化镜像

  • docker commit :从容器创建一个新的镜像。
  • docker commit \[OPTIONS] CONTAINER \[REPOSITORY\[:TAG]]
    • -a :提交的镜像作者
    • -c :使用Dockerfile指令来创建镜像
    • -m :提交时的说明文字
    • -p :在commit时,将容器暂停
  • 停止容器后不会自动删除这个容器,除非在启动容器的时候指定了 --rm 标志,注意--rm标志不能与-d(或--detach)标志一起使用。因为-d标志使容器在后台运行,而--rm标志只会在容器退出时删除它。
  • 使用 docker ps -a 命令查看 Docker 主机上包含停止的容器在内的所有容器
  • 停止状态的容器的可写层仍然占用磁盘空间。要清理可以使用 docker container prune 命令
#将一个正在运行的 Docker 容器保存为一个新的 Docker 镜像
docker container commit -m "我的nginx" -a "caixukun" 3695dc5b9c2d caixukun/mynginx:v1

docker image ls
docker container run caixukun/mynginx /bin/bash
docker container rm b2839066c362
docker container prune
docker image rmi c79ef5b3f5fc

11. 制作Dockerfile

  • Docker 的镜像是用一层一层的文件组成的
  • docker inspect 命令可以查看镜像或者容器,其中 Layers 就是镜像的层文件,只读不能修改。基于镜像创建的容器会共享这些文件层
docker inspect centos

11.1 编写Dockerfile

  • -t --tag list 镜像名称
  • -f --file string 指定Dockerfile文件的位置
指令含义示例
FROM构建的新镜像是基于哪个镜像FROM centos:7
MAINTAINER镜像维护者姓名或邮箱地址MAINTAINER caixukun
RUN构建镜像时运行的shell命令RUN yum install httpd
CMDCMD 设置容器启动后默认执行的命令及其参数,但 CMD 能够被 docker run 后面跟的命令行参数替换CMD /usr/sbin/sshd -D
EXPOSE声明容器运行的服务器端口EXPOSE 80 443
ENV设置容器内的环境变量ENV MYSQL_ROOT_PASSWORD 123456
ADD拷贝文件或目录到镜像中,如果是URL或者压缩包会自动下载和解压ADD xxx.com/html.tar.gz /var/www.html, ADD html.tar.gz /var/www/html
COPY拷贝文件或目录到镜像COPY ./start.sh /start.sh
ENTRYPOINT配置容器启动时运行的命令ENTRYPOINT /bin/bash -c '/start.sh'
VOLUME指定容器挂载点到宿主自动生成的目录或其它容器VOLUME ["/var/lib/mysql"]
USER为 RUN CMD和ENTRYPOINT执行命令指定运行用户USER caixukun
WORKDIR为RUN CMD ENTRYPOINT COPY ADD 设置工作目录WORKDIR /data
HEALTHCHECK健康检查HEALTHCHECK --interval=5m --timeout=3s --retries=3 CMS curl -f htp://localhost
ARG在构建镜像时指定一些参数ARG user

cmd给出的是一个容器的默认的可执行体。也就是容器启动以后,默认的执行的命令。重点就是这个"默认"。意味着,如果docker run没有指定任何的执行命令或者dockerfile里面也没有entrypoint,那么,就会使用cmd指定的默认的执行命令执行。同时也从侧面说明了entrypoint的含义,它才是真正的容器启动以后要执行的命令

11.2 dockerignore

表示要排除,不要打包到image中的文件路径,例如可以往里面写:

node\_modules
.git

11.3 Dockerfile

11.3.1 安装node

  • nvm
wget -qO- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash
source /root/.bashrc
nvm install v14.21.3 #试了下装不了太高版本
node -v
npm i nrm -g

11.3.2 安装express项目生成器

npm install express-generator -g
express app

11.3.3 Dockerfile

FROM node
COPY ./app /app
WORKDIR /app
RUN npm install
EXPOSE 3000
  • FROM 表示该镜像继承的镜像
  • COPY 是将当前目录下的app目录下面的文件都拷贝到image里的/app目录中
  • WORKDIR 指定工作路径,类似于执行 cd 命令
  • RUN npm install 在/app目录下安装依赖,安装后的依赖也会打包到image目录中
  • EXPOSE 暴露3000端口,允许外部连接这个端口

11.4 创建image

docker build -t express-demo .
  • -t 用来指定image镜像的名称,后面还可以加冒号指定标签,如果不指定默认就是latest
  • . 表示Dockerfile文件的位置, .表示在当前路径

11.5 使用新的镜像运行容器

docker container run -p 3100:3000 -it express-demo /bin/bash
npm start
  • -p 参数是将容器的3000端口映射为本机的3333端口
  • -it 参数是将容器的shell容器映射为当前的shell,在本机容器中执行的命令都会发送到容器当中执行
  • express-demo image的名称
  • /bin/bash 容器启动后执行的第一个命令,这里是启动了bash容器以便执行脚本

11.6 CMD

Dockerfile文件里可以增加

CMD npm start

然后重新制作镜像

docker build -t express-demo .

这里RUNCMD的区别就是:

  • RUN 在 image 文件的构建阶段执行,执行结果都会打包进入 image 文件,一个 Dockerfile 可以包含多个RUN命令;
  • CMD 则是在容器启动后执行,一个 Dockerfile 只能有一个 CMD。

注意:指定了CMD命令以后,docker container run命令就不能附加命令了(比如前面的/bin/bash),否则它会覆盖CMD命令

11.7 发布image

  • 注册账户
  • docker tag SOURCE_IMAGE[:TAG] TARGET_IMAGE[:TAG]
docker login
docker tag express-demo caixukun/express-demo:v1
docker push caixukun/express-demo:v1

这个命令会将 caixukun/express-demo:v1 镜像推送到 Docker Hub

12. 数据盘

删除容器的时候,容器层里创建的文件也会被删除掉,如果想持久化数据,比如 Web 服务器的日志,可以为容器创建一个数据盘(volume)。

12.1 使用容器 : 管理和存储数据

  • 容器存在的数据管理问题

    • 沙盒文件系统是跟随容器生命周期所创建和移除的,数据无法直接被持久化存储。
    • 由于容器隔离,我们很难从容器外部获得或操作容器内部文件中的数据。
  • docker 提供的解决方案

    • Docker 容器文件系统是基于 UnionFS , 实现了目录挂载功能 . 所以我们只需要将宿主操作系统中,文件系统里的文件或目录挂载到容器中,便能够让容器内外共享这个文件。
    • UnionFS 带来的读写性能损失是可以忽略不计的.

12.2 挂载方式

  • Bind Mount

    • 能够直接将宿主操作系统中的目录和文件挂载到容器内的文件系统中,通过指定容器外的路径和容器内的路径,就可以形成挂载映射关系,在容器内外对文件的读写,都是相互可见的
  • Volume

    • 也是从宿主操作系统中挂载目录到容器内,只不过这个挂载的目录由 Docker 进行管理我们只需要指定容器内的目录,不需要关心具体挂载到了宿主操作系统中的哪里。
  • Tmpfs Mount

    • 支持挂载系统内存中的一部分到容器的文件系统里,不过由于内存和容器的特征,它的存储并不是持久的,其中的内容会随着容器的停止而消失。

image.png

12.3 volume

  • /var/lib/docker/volumes/ 是 Docker 在宿主机上存储卷数据的默认位置
  • 如果没有指定卷,则会自动创建
  • 建议使用 --mount,更通用

12.3.1 创建数据卷

docker volume --help
docker volume create nginx-vol
docker volume ls
docker volume inspect nginx-vol

#/usr/share/nginx/html 是默认的Nginx web服务器文档根目录的路径
#把nginx-vol数据卷挂载到/usr/share/nginx/html,挂载后容器内的文件会同步到数据卷中
docker run -d  --name=nginx1 --mount src=nginx-vol,dst=/usr/share/nginx/html nginx
#也可以使用-v命令,更加简便
docker run -d  --name=nginx2  -v nginx-vol:/usr/share/nginx/html -p 3000:80 nginx

如果你想要使用本地主机上的目录作为卷,你的命令应该类似于这样(假设你在 /path/on/host 上有一个目录)

docker run -d --name=nginx2 -v /path/on/host:/usr/share/nginx/html -p 3000:80 nginx

12.3.2 删除数据卷

#停止容器
docker container stop nginx1 
#删除容器
docker container rm nginx1 
#删除数据卷
docker volume rm nginx-vol  

12.3.3 管理数据盘

docker volume ls #列出所有的数据盘
docker volume ls -f dangling=true #列出已经孤立的数据盘
docker volume rm xxxx #删除数据盘
docker volume ls      #列出数据盘

12.4 Bind mounts

  • 此方式与Linux系统的mount方式很相似,即是会覆盖容器内已存在的目录或文件,但并不会改变容器内原有的文件,当umount后容器内原有的文件就会还原
  • 创建容器的时候我们可以通过-v或--volumn给它指定一下数据盘
  • bind mounts 可以存储在宿主机系统的任意位置
  • 如果源文件/目录不存在,不会自动创建,会抛出一个错误
  • 如果挂载目标在容器中非空目录,则该目录现有内容将被隐藏

12.4.1 默认数据盘

  • -v 参数两种挂载数据方式都可以用
docker run -v /mnt:/mnt -it --name logs centos bash
cd /mnt
echo 1 > 1.txt
exit

执行docker inspect logs可以看到:

"Mounts":[
    {
        "Source":"/mnt/sda1/var/lib/docker/volumes/dea6a8b3aefafa907d883895bbf931a502a51959f83d63b7ece8d7814cf5d489/\_data",
        "Destination": "/mnt",
    }
]
  • Source的值就是我们给容器指定的数据盘在主机上的位置
  • Destination的值是这个数据盘在容器上的位置

12.4.2 指定数据盘

mkdir \~/data
docker run -v \~/data:/mnt -it --name logs2 centos bash
cd /mnt
echo 3 > 3.txt
exit
cat \~/data/3.txt
  • ~/data:/mnt 把当前用户目录中的data目录映射到/mnt上

12.4.3 指定数据盘容器

docker create \[OPTIONS] IMAGE \[COMMAND] \[ARG...] #创建一个新的容器但不启动
docker create -v /mnt:/mnt --name logger centos
docker run --volumes-from logger --name logger3 -i -t centos bash
cd /mnt
touch logger3
docker run --volumes-from logger --name logger4 -i -t centos bash
cd /mnt
touch logger4

13. 网络

  • 安装Docker时,它会自动创建三个网络,bridge(创建容器默认连接到此网络)、 none 、host

    • None:该模式关闭了容器的网络功能,对外界完全隔离
    • host:容器将不会虚拟出自己的网卡,配置自己的IP等,而是使用宿主机的IP和端口。
    • bridge:桥接网络,此模式会为每一个容器分配IP
  • 可以使用该--network标志来指定容器应连接到哪些网络

13.1 bridge(桥接)

  • bridge网络代表所有Docker安装中存在的网络
  • 除非你使用该docker run --network=选项指定,否则Docker守护程序默认将容器连接到此网络
  • bridge模式使用 --net=bridge 指定,默认设置
docker network ls #列出当前的网络
docker inspect bridge #查看当前的桥连网络
docker run -d --name nginx1 nginx
docker run -d --name nginx2 --link nginx1 nginx
docker exec -it nginx2 bash
apt update
apt install -y inetutils-ping  #ping
apt install -y dnsutils        #nslookup
apt install -y net-tools       #ifconfig
apt install -y iproute2        #ip
apt install -y curl            #curl
cat /etc/hosts
ping nginx1

13.2 none

none模式使用--net=none指定

# --net 指定无网络
docker run -d --name nginx_none --net none nginx
docker inspect none
docker exec -it nginx_none bash
ip addr

13.3 host

host模式使用 --net=host 指定

docker run -d --name nginx_host --net host nginx
docker inspect host
docker exec -it nginx_host bash
ip addr

13.4 端口映射

# 查看镜像里暴露出的端口号
docker image inspect nginx
#"ExposedPorts": {"80/tcp": {}}

# 让宿主机的8080端口映射到docker容器的80端口
docker run -d --name port_nginx -p 8080:80  nginx

# 查看主机绑定的端口
docker container port port_nginx

13.5 指向主机的随机端口

docker run -d --name random_nginx --publish 80 nginx
docker port random_nginx

docker run -d --name randomall_nginx --publish-all nginx
docker run -d --name randomall_nginx --P nginx

13.6 创建自定义网络

  • 可以创建多个网络,每个网络IP范围均不相同
  • docker的自定义网络里面有一个DNS服务,可以通过容器名称访问主机
# 创建自定义网络
docker network create --driver bridge myweb
# 查看自定义网络中的主机
docker network inspect myweb
# 创建容器的时候指定网络
docker run -d --name mynginx1  --net myweb nginx
docker run -d --name mynginx2  --net myweb nginx
docker exec -it mynginx2 bash
ping mynginx1 #会发现同一个网络可以ping通

13.7 连接到指定网络

docker run -d --name mynginx3   nginx
docker network connect  myweb mynginx3
docker network disconnect myweb mynginx3

13.8 移除网络

docker network rm myweb

14.compose

  • 如果说 Dockerfile 是将容器内运行环境的搭建固化下来,那么 Docker Compose 我们就可以理解为将多个容器运行的方式和配置固化下来。Compose 通过一个配置文件来管理多个Docker容器

  • 在配置文件中,所有的容器通过services来定义,然后使用docker-compose脚本来启动、停止和重启应用和应用中的服务以及所有依赖服务的容器

  • 最后,运行docker-compose up,Compose 将启动并运行整个应用程序

  • services 可以定义需要的服务,每个服务都有自己的名字、使用的镜像、挂载的数据卷所属的网络和依赖的其它服务

  • networks 是应用的网络,在它下面可以定义使用的网络名称、类型

  • volumes是数据卷,可以在此定义数据卷,然后挂载到不同的服务上面使用

14.1 安装compose

yum -y install epel-release
yum -y install python-pip
yum clean all
pip install docker-compose

14.2 编写docker-compose.yml

  • 在 docker-compose.yml 中定义组成应用程序的服务,以便它们可以在隔离的环境中一起运行
  • 空格缩进表示层次
version: '2'  # 指定 docker-compose 文件的版本  
  
services:  # 定义服务  
  web:  # 服务的名称  
    build: .  # 从当前目录下的 Dockerfile 构建镜像  
    ports:  
      - "5000:5000"  # 将容器的 5000 端口映射到主机的 5000 端口  
    volumes:  
      - .:/code  # 将当前目录挂载到容器的 /code 目录  
    depends_on:  
      - db  # 指定该服务依赖于名为 db 的服务  
  
  db:  # 另一个服务的名称  
    image: postgres  # 使用预构建的 postgres 镜像  
    volumes:  
      - db_data:/var/lib/postgresql/data  # 使用名为 db_data 的卷来存储数据  
  
volumes:  # 定义卷  
  db_data: {}  # 创建一个名为 db_data 的卷,无需指定驱动或选项  
  
networks:  # 定义网络(可选)  
  default:  # 使用默认网络,也可以自定义网络配置  
    driver: bridge  # 使用 bridge 驱动

14.3 启动服务

docker会创建默认的网络

命令服务
docker-compose up启动所有的服务
docker-compose up -d后台启动所有的服务
docker-compose ps打印所有的容器
docker-compose stop停止所有服务
docker-compose logs -f持续跟踪日志
docker-compose exec nginx1 bash进入nginx1服务系统
docker-compose rm nginx1删除服务容器
docker network ls查看网络网络不会删除
docker-compose down删除所有的网络和容器

删除所有的容器: docker container rm docker container ps -a -q

14.4 网络互ping

docker-compose up -d
docker-compose exec nginx1 bash
apt update && apt install -y inetutils-ping

#可以通过服务的名字连接到对方 ping nginx2

14.5 配置数据卷

  • networks 指定自定义网络
  • volumes 指定数据卷
  • 数据卷在宿主机的位置 /var/lib/docker/volumes/nginx-compose_data/_data
version: '3'
services: 
    nginx1: 
        image: nginx 
        ports: 
            - "8081:80" 
        networks: 
            - "newweb" 
        volumes: 
            - "data:/data" 
            - "./nginx1:/usr/share/nginx/html" 
    nginx2: 
        image: nginx 
        ports: 
            - "8082:80" 
        networks: 
            - "default" 
        volumes: 
            - "data:/data" 
            - "./nginx2:/usr/share/nginx/html" 
    nginx3: 
        image: nginx 
        ports: 
            - "8083:80" 
        networks: 
            - "default" 
            - "newweb" 
        volumes: 
            - "data:/data" 
            - "./nginx3:/usr/share/nginx/html" 
networks: 
    newweb: 
        driver: bridge 
volumes: 
    data: 
        driver: local
docker exec nginx-compose_nginx1_1 bash 
cd /data
touch 1.txt 
exit 
cd /var/lib/docker/volumes/nginx-compose_data/_data 
ls

15. node项目

创建一个 nodeapp 目录,作为一个用 Docker 搭建的本地 Node.js 应用开发与运行环境。

15.1 服务分类

  • db:使用 mariadb 作为应用的数据库
  • node:启动node服务
  • web:使用 nginx 作为应用的 web 服务器

15.2 nodeapp下的目录结构

文件说明
docker-compose.yml定义本地开发环境需要的服务
images/nginx/config/default.confnginx 配置文件
images/node/Dockerfilenode的Dockfile配置文件
images/node/web/package.json项目文件
images/node/web/public/index.html静态首页
images/node/web/server.jsnode服务|
├── docker-compose.yml
└── images
    ├── nginx
    │   └── config
    │       └── default.conf
    └── node
        ├── Dockerfile
        └── web
            ├── package.json
            ├── public
            │   └── index.html
            └── server.js

15.2.1 docker-compose.yml

version: '2'  
# 指定 Docker Compose 文件的版本为 2  
 
services:  
   # 定义 Node.js 服务  
   node:   
       # 从指定的目录构建 Docker 镜像  
       build:   
           context: ./images/node  # 构建上下文目录  
           dockerfile: Dockerfile  # Dockerfile 的名称  
       # 指定该服务依赖于 db 服务  
       depends_on:   
           - db  
 
   # 定义 Nginx 服务  
   web:   
       # 使用预构建的 nginx 镜像  
       image: nginx   
       # 将主机的 8080 端口映射到容器的 80 端口  
       ports:   
           - "8080:80"  
       # 将两个目录挂载到容器中  
       volumes:   
           - ./images/nginx/config:/etc/nginx/conf.d  # Nginx 配置文件目录  
           - ./images/node/web/public:/public  # Node.js 应用的 public 目录  
       # 指定该服务依赖于 node 服务  
       depends_on:   
           - node  
 
   # 定义 MariaDB 数据库服务  
   db:   
       # 使用预构建的 mariadb 镜像  
       image: mariadb   
       # 设置环境变量,用于配置数据库  
       environment:   
           MYSQL_ROOT_PASSWORD: "root"  # root 用户的密码  
           MYSQL_DATABASE: "node"  # 创建名为 node 的数据库  
           MYSQL_USER: "cxk"  # 创建一个名为 cxk 的用户  
           MYSQL_PASSWORD: "123456"  # 设置 cxk 用户的密码  
       # 将数据目录挂载到名为 db 的卷上,以便数据持久化  
       volumes:   
           - db:/var/lib/mysql  
 
# 定义了一个名为 db 的卷,用于存储 MariaDB 的数据  
volumes:   
   db:   
       driver: local  # 指定卷使用本地文件系统

15.2.2 server.js

images/node/web/server.js文件中起一个服务

let http = require('http');
var mysql = require('mysql');
var connection = mysql.createConnection({
    host: 'db',
    user: 'cxk',
    password: '123456',
    database: 'node'
});
connection.connect();
let server = http.createServer(function (req, res) {
    connection.query('SELECT 2 + 2 AS solution', function (error, results, fields) {
        if (error) throw error;
        res.end('' + results[0].solution);
    });
});
server.listen(3000);

15.2.3 package.json

指定images/node/web/package.json的内容:

{
    "scripts": {
        "start": "node server.js"
    },
}

并通过npm安装 mysql

15.2.4 images/node/Dockerfile

FROM node
MAINTAINER caixukun <caixukun@163.com>
COPY ./web /web
WORKDIR /web
RUN npm install
CMD npm start

15.2.5 images/nginx/config/default.conf

upstream backend {
    server node:3000;
}
server {
    listen 80;
    server_name localhost;
    root /public;
    index index.html index.htm;

    location /api {
        proxy_pass http://backend;
    }
}