今天,我们将学习 Docker Compose,这是一个可以简化容器化应用程序的管理的强大工具。通过利用它,我们可以轻松编排和部署复杂的应用程序栈。
Docker Compose
Docker Compose 是一个命令行工具,允许我们定义和管理多容器应用程序。借助 Docker Compose,我们可以使用简单的 YAML 文件描述应用程序的服务,网络和数据卷。它提供了一种声明性方法来定义应用程序的基本架构要求,从而更轻松地在开发、测试和生产的不同阶段管理和重现我们的环境。
安装 Docker Compose
首先,我们需要确保我们的系统上安装了 Docker Compose。以下时在 Ubuntu 系统上安装 Docker Compose 的步骤:
- 更新包索引,并安装最新版本的 Docker Compose:
sudo apt-get update
sudo apt-get install docker-compose-plugin
- 通过检查版本来验证 Docker Compose 是否已正确安装:
docker compose version
如果安装成功,我们会看到 Docker Compose 的版本信息。
Docker Compose 命令行
docker-compose 命令提供了许多子命令来使用 docker-compose 管理 Docker 容器。下面我们来详细了解下各个子命令。
build
build 子命令用于为已定义构建的服务构建镜像。
代码语言:javascript复制docker-compose build # 构建所有服务
docker-compose build web # 构建单个服务
up
用于在当前目录的docker-compose.yaml
文件中创建具有可用服务的 docker 容器。使用-d
参数以守护进程启动容器。
docker-compose up -d # 创建所有容器
docker-compose up -d web # 创建单个容器
down
用于停止并删除配置文件中定义的服务的所有容器、网络和关联镜像。
代码语言:javascript复制docker-compose down # 停止所有容器
docker-compose down web # 停止单个容器
ps
用于列出为配置文件中定义的服务创建的所有容器及其状态、端口绑定和命令。
代码语言:javascript复制docker-compose ps
exec
用于对正在运行的容器执行命令。例如,列出与 Web 服务关联的容器中的文件。
代码语言:javascript复制docker-compose exec web ls -l
start
用于启动配置文件中定义的服务的已停止容器。
代码语言:javascript复制docker-compose start # 启动所有容器
docker-compose start web # 启动单个容器
stop
用于停止运行配置文件中定义的服务的容器。
代码语言:javascript复制docker-compose stop # 停止所有容器
docker-compose stop web # 停止单个容器
restart
用于重新启动配置文件中定义的服务的容器。
代码语言:javascript复制docker-compose restart # 重启所有容器
docker-compose restart web # 重启单个容器
pause
用于暂停配置文件中定义的服务的运行容器。
代码语言:javascript复制docker-compose pause # 暂停所有容器
docker-compose pause web # 暂停单个容器
unpause
用于为配置文件中定义的服务启动暂停的容器。
代码语言:javascript复制docker-compose pause # 启动所有暂停的容器
docker-compose pause # 启动单个暂停的容器
rm
用于删除配置文件中定义的服务的已停止的容器。
代码语言:javascript复制docker-compose rm # 删除所有容器
docker-compose web # 删除单个容器
在没有 sudo 下运行 Docekr 命令
默认情况下,运行 Docker 命令需要 sudo 或 root 权限。但是,可以授予我们的用户在不适用 sudo 的情况下运行 Docker 命令的权限。以下时实现这一目标的方法:
- 通过运行以下命令将用户添加到
docker
组(将<username>
替换为你的实际用户名):
sudo usermod -aG docker <username>
- 将用户添加到
docker
组后,需要重新启动系统才能使更改生效。通过运行以下命令重新启动你的机器:
sudo reboot
重新启动后,我们就能够在没有 sudo 的情况下运行 Docker 命令。
什么是 YAML
YAML,全程”YAML Ain't Markup Language“,是一种流行的数据序列化格式,广泛应用于 DevOps 领域,用于创建配置文件。它提供了人类可读且机器友好的语法,允许开发人员和系统管理员清晰,简洁地定义结构化数据。
DevOps 中 YAML 的主要用例之一是用于配置和定义应用程序设置和环境。例如,在 Docker Compose 中,YAML 文件用于指定运行多容器应用程序所需的服务、网络、数据卷和其他配置。
YAML 文件利用缩进和简单的语法来分层表示数据,使其易于理解和使用。这种可读性是 YAML 的关键优势之一,因为它使我们和机器能够轻松地解析和解释数据。
此外,YAML 支持各种数据类型,例如标量(字符串、数字、布尔值)、序列(数组、列表)和映射(键值对)。这种灵活性允许表示应用程序不同组件之间的复杂配置和关系。
实践一 创建 docker-compose.yaml
浏览以下docker-compose.yaml
文件并了解它如何设置环境,配置服务,在容器之间建立链接以及使用环境变量:
version: "3.3"
services:
web:
image: nginx:latest
ports:
- "80:80"
db:
image: mysql
ports:
- "3306:3306"
environment:
- "MYSQL_ROOT_PASSWORD=test@123"
version: "3.3"
行只当正在使用的 Docker Compose 文件语法的版本。- 在
services
部分下,定义了连个服务:web
和db
。每个服务代表一个容器。 web
服务使用nginx: latest
镜像,这是一个流行的 Web 服务器。它可以通过定义为ports: - "80:80"
的端口映射来访问,其中容器的端口 80 映射到主机的端口 80。这语序通过主机上的http://localhost
访问容器内运行的 Web 服务器。db
服务使用mysql
镜像,这是一个广泛使用的关系数据库管理系统,与web
服务类似,它也将端口映射定义为ports: - "3306:3306"
,允许通过localhost
在端口 3306 上访问容器内运行的 MySQL 数据库服务器主机。- 此外,
db
服务将MYSQL_ROOT_PASSWORD
环境变量设置为test@123
。这会设置容器内 MySQL 数据库的 root 密码,以确保安全访问。
通过在docker-compose.yaml
文件所在的目录中运行docker-compose up
命令,Docker Compose 将创建并启动定义的服务。它自动管理容量之间的网络并设置执行的环境变量。
root@huang-ubuntu:~/Codes/docker-compose-case# docker compose up
[ ] Running 11/11
✔ db 10 layers [⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿] 0B/0B Pulled 266.6s
✔ 8e0176adc18c Pull complete 14.9s
✔ 2d2c52718f65 Pull complete 1.7s
✔ d88d03ce139b Pull complete 2.8s
✔ 4a7d7f11aa1e Pull complete 6.3s
✔ ce5949193e4c Pull complete 4.8s
✔ f7f024dfb329 Pull complete 11.1s
✔ 5fc3c840facc Pull complete 31.5s
✔ 509068e49488 Pull complete 12.9s
✔ cbc847bab598 Pull complete 46.1s
✔ 942bef62a146 Pull complete 17.5s
[ ] Running 3/3
✔ Network docker-compose-case_default Created 1.2s
✔ Container docker-compose-case-web-1 Created 2.7s
✔ Container docker-compose-case-db-1 Created 2.7s
Attaching to docker-compose-case-db-1, docker-compose-case-web-1
...
实践二 非 root 身份运行容器
从公共存储库(如 Docker Compose)中提取预先存在的 Docker 镜像并在本地计算机上运行。以非 root 身份运行容器。确保在授予用户权限后重新启动实例。
使用 docke pull 命令,后跟镜像名称和标签来拉取镜像。
代码语言:javascript复制root@huang-ubuntu:~# docker pull nginx:latest
latest: Pulling from library/nginx
Digest: sha256:10d1f5b58f74683ad34eb29287e07dab1e90f10af243f151bb50aa5dbb4d62ee
Status: Image is up to date for nginx:latest
docker.io/library/nginx:latest
使用docker inspect <container-id>
命令检查容器的运行进程和暴露的端口。
root@huang-ubuntu:~# docker run -d -p 80:80 nginx
df803daffebe52cdac0f7dc1f08fb158f3fb8d9a20371f016f9a50f995c3b518
root@huang-ubuntu:~# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
df803daffebe nginx "/docker-entrypoint.…" 12 seconds ago Up 9 seconds 0.0.0.0:80->80/tcp, :::80->80/tcp vibrant_shannon
root@huang-ubuntu:~# docker inspect df803daffebe
[
{
"Id": "df803daffebe52cdac0f7dc1f08fb158f3fb8d9a20371f016f9a50f995c3b518",
"Created": "2023-12-07T10:33:20.171973934Z",
"Path": "/docker-entrypoint.sh",
"Args": [
"nginx",
"-g",
"daemon off;"
],
"State": {
"Status": "running",
使用docker logs <container-id>
命令查看容器的日志输出。
root@huang-ubuntu:~# docker logs df803daffebe
/docker-entrypoint.sh: /docker-entrypoint.d/ is not empty, will attempt to perform configuration
/docker-entrypoint.sh: Looking for shell scripts in /docker-entrypoint.d/
/docker-entrypoint.sh: Launching /docker-entrypoint.d/10-listen-on-ipv6-by-default.sh
10-listen-on-ipv6-by-default.sh: info: Getting the checksum of /etc/nginx/conf.d/default.conf
10-listen-on-ipv6-by-default.sh: info: Enabled listen on IPv6 in /etc/nginx/conf.d/default.conf
/docker-entrypoint.sh: Sourcing /docker-entrypoint.d/15-local-resolvers.envsh
/docker-entrypoint.sh: Launching /docker-entrypoint.d/20-envsubst-on-templates.sh
/docker-entrypoint.sh: Launching /docker-entrypoint.d/30-tune-worker-processes.sh
/docker-entrypoint.sh: Configuration complete; ready for start up
2023/12/07 10:33:24 [notice] 1#1: using the "epoll" event method
2023/12/07 10:33:24 [notice] 1#1: nginx/1.25.3
2023/12/07 10:33:24 [notice] 1#1: built by gcc 12.2.0 (Debian 12.2.0-14)
2023/12/07 10:33:24 [notice] 1#1: OS: Linux 5.15.0-89-generic
2023/12/07 10:33:24 [notice] 1#1: getrlimit(RLIMIT_NOFILE): 1048576:1048576
2023/12/07 10:33:24 [notice] 1#1: start worker processes
2023/12/07 10:33:24 [notice] 1#1: start worker process 29
使用docker stop <container-id>
和docker start <container-id>
命令来停止和启动容器。
root@huang-ubuntu:~# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
df803daffebe nginx "/docker-entrypoint.…" 5 minutes ago Up 5 minutes 0.0.0.0:80->80/tcp, :::80->80/tcp vibrant_shannon
root@huang-ubuntu:~# docker stop df803daffebe
df803daffebe
root@huang-ubuntu:~# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
root@huang-ubuntu:~# docker start df803daffebe
df803daffebe
root@huang-ubuntu:~# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
df803daffebe nginx "/docker-entrypoint.…" 6 minutes ago Up 5 seconds 0.0.0.0:80->80/tcp, :::80->80/tcp vibrant_shannon
完成后,使用docker rm <container-id>
来删除容器,删除的前提使容器需要停止运行。
root@huang-ubuntu:~# docker stop df803daffebe
df803daffebe
root@huang-ubuntu:~# docker rm df803daffebe
df803daffebe
root@huang-ubuntu:~# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
实践三 使用 compose 文件来创建 Jenkins 和 Portainer 容器
docker-composer.yaml 文件内容如下:
代码语言:javascript复制version: "3.9"
services:
portainer:
container_name: portainer
image: portainer/portainer-ce:latest
restart: always
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- portainer_data:/data
ports:
- 9000:9000
jenkins:
container_name: jenkins
image: jenkins/jenkins:lts
restart: always
volumes:
- jenkins_data:/var/jenkins_home
ports:
- 8080:8080
- 50000:50000
volumes:
portainer_data:
jenkins_data:
version
字段指定正在使用的 Docker Compose 文件格式的版本。- 在
services
部分下,我们定义了两个服务:portainer
和Jenkins
。 - 对于
portainer
服务:image
字段指定要使用的 Portaine 镜像。volumes
字段挂在 Docker 套接字和用于 Portainer 数据持久化的数据卷。ports
字段将容器的端口 9000 映射到主机的端口 9000。
- 对于
jenkins
服务:image
字段指定要使用的 Jenkins 镜像。volumes
字段挂在一个用于 Jenkins 数据持久化的数据卷。
ports
字段将容器的端口 8080 和 50000 映射到主机上的相同端口。
这个示例设置 Portainer 来管理 Docker 容器,并设置 Jenkins 来实现持续集成和持续交付(CI/CD)流程。我们可以在主机的端口 9000 上访问 Portainer,在端口 8080 上访问 Jenkins。