在 AWS 中使用 Docker Swarm 进行容器编排与管理
1. 部署栈与监控服务
当部署栈并监控
collectstatic
服务时,可能会注意到一些初始失败情况。以下是具体操作步骤:
1. 部署栈:
docker stack deploy --with-registry-auth -c stack.yml todobackend
此命令会创建网络和服务,如
todobackend_default
、
todobackend_net
、
todobackend_collectstatic
和
todobackend_app
。
2. 监控
collectstatic
服务:
docker service ps todobackend_collectstatic
该命令不仅会显示当前服务状态,还会显示服务历史。例如,可能会看到第一次运行
collectstatic
服务在 32 秒前失败,之后 Docker Swarm 尝试重启该服务并成功。由于重启策略设置为失败时重启,且服务最终无错误退出,Swarm 不会再次尝试启动该服务。
若此时浏览外部负载均衡器 URL,会发现
todobackend
应用的静态内容已正确呈现,但数据库配置错误仍然存在。
2. 创建持久存储
接下来,将注意力转移到应用数据库,它是
todobackend
应用的重要支持组件。在 AWS 中,可以使用 Docker for AWS 解决方案来支持持久存储。
Cloudstor 卷插件除了支持 EFS 支持的卷外,还支持可迁移的 Elastic Block Store (EBS) 卷。可迁移意味着当 Docker Swarm 决定将容器从一个节点迁移到另一个节点时,插件会自动将当前分配给容器的 EBS 卷迁移到新节点。迁移过程根据不同场景有所不同:
-
新节点在同一可用区
:插件会简单地从现有节点的 EC2 实例上分离卷,并将其重新附加到新节点。
-
新节点在不同可用区
:插件会对现有卷进行快照,然后从该快照在新可用区创建新卷,完成后销毁之前的卷。
需要注意的是,Docker 仅支持对可迁移 EBS 支持的卷进行单容器访问,即任何时候只能有一个容器对该卷进行读写操作。若需要共享卷访问,则必须创建 EFS 支持的共享卷。
以下是定义一个名为
data
的卷来存储
todobackend
数据库,并创建一个运行 MySQL 并附加到该卷的
db
服务的配置:
version: '3.6'
networks:
net:
driver: overlay
volumes:
public:
driver: cloudstor:aws
driver_opts:
backing: shared
data:
driver: cloudstor:aws
driver_opts:
backing: relocatable
size: 10
ebstype: gp2
services:
app:
image: 385605022855.dkr.ecr.us-east-1.amazonaws.com/docker-in-aws/todobackend
ports:
- target: 8000
published: 80
networks:
- net
volumes:
- public:/public
collectstatic:
image: 385605022855.dkr.ecr.us-east-1.amazonaws.com/docker-in-aws/todobackend
volumes:
- public:/public
db:
image: mysql:5.7
environment:
MYSQL_DATABASE: todobackend
MYSQL_USER: todo
MYSQL_PASSWORD: password
MYSQL_ROOT_PASSWORD: password
networks:
- net
volumes:
- data:/var/lib/mysql
command:
- --ignore-db-dir=lost+found
deploy:
replicas: 1
placement:
constraints:
- node.role == manager
具体操作步骤如下:
1. 创建名为
data
的卷,并将驱动配置为
cloudstor:aws
,指定支持可迁移的 EBS 卷,大小为 10 GB,EBS 类型为 gp2(SSD)存储。
2. 定义一个新的
db
服务,运行官方 MySQL 5.7 镜像,将其附加到之前定义的
net
网络,并将
data
卷挂载到
/var/lib/mysql
,这是 MySQL 存储数据库的位置。
3. 由于 Cloudstor 插件将挂载的卷格式化为 ext4,格式化过程中会自动创建一个名为
lost+found
的文件夹,这会导致 MySQL 容器认为存在名为
lost+found
的现有数据库而中止。为解决此问题,传递
--ignore-db-dir
标志,让 MySQL 守护进程忽略该文件夹。
4. 定义一个放置约束,强制
db
服务部署到 Swarm 管理器,以便后续通过更改此约束来测试
data
卷的可迁移特性。
部署栈并监控
db
服务时,由于数据卷初始化,服务启动可能需要一些时间:
docker stack deploy --with-registry-auth -c stack.yml todobackend
docker service ps todobackend_db --format "{{ .Name }} ({{ .ID }}): {{ .CurrentState }}"
为验证 EBS 卷是否实际创建,可以使用 AWS CLI:
aws ec2 describe-volumes --filters Name=tag:CloudstorVolumeName,Values=* \
--query "Volumes[*].{ID:VolumeId,Zone:AvailabilityZone,Attachment:Attachments,Tag:Tags}"
3. 迁移 EBS 卷
成功创建并附加 EBS 支持的数据卷后,可以通过更改
db
服务的放置约束来测试将其从管理器节点迁移到工作节点:
version: '3.6'
services:
db:
image: mysql:5.7
environment:
MYSQL_DATABASE: todobackend
MYSQL_USER: todo
MYSQL_PASSWORD: password
MYSQL_ROOT_PASSWORD: password
networks:
- net
volumes:
- data:/var/lib/mysql
command:
- --ignore-db-dir=lost+found
deploy:
replicas: 1
placement:
constraints:
- node.role == worker
部署更改后,可以观察 EBS 迁移过程:
1. 定义查询命令:
volumes='aws ec2 describe-volumes --filters Name=tag:CloudstorVolumeName,Values=* \
--query "Volumes[*].{ID:VolumeId,State:Attachments[0].State,Zone:AvailabilityZone}" \
--output text'
snapshots='aws ec2 describe-snapshots --filters Name=status,Values=pending \
--query "Snapshots[].{Id:VolumeId,Progress:Progress}" --output text'
- 部署更改:
docker stack deploy --with-registry-auth -c stack.yml todobackend
- 观察迁移过程:
eval $volumes
eval $snapshots
通过多次运行
eval $volumes
命令,可以观察到卷从
us-east-1b
可用区的管理器节点迁移到
us-east-1a
可用区的工作节点的过程。最后,使用
docker service ps
命令验证
db
服务是否再次正常运行。
由于当前在栈文件中使用明文密码的密码管理策略不理想,且数据库已使用这些密码初始化,在继续之前需要拆除栈:
docker stack rm todobackend
docker volume ls
docker volume rm todobackend_public
docker volume rm todobackend_data
需要注意的是,拆除栈时,必须手动删除栈中使用的任何卷。
4. 使用 Docker 机密进行秘密管理
在之前创建
db
服务的示例中,未将应用与
db
服务集成,部分原因是当前以明文形式配置
db
服务的密码,这并不理想。
Docker Swarm 包含一个名为 Docker 机密的功能,它为在 Docker Swarm 集群上运行的应用提供安全的秘密管理解决方案。机密存储在名为 raft 日志的内部加密存储机制中,并复制到集群中的所有节点,确保任何被授予访问机密权限的服务和相关容器都能安全地访问该机密。
创建 Docker 机密的步骤如下:
openssl rand -base64 32 | docker secret create todobackend_mysql_password -
openssl rand -base64 32 | docker secret create todobackend_mysql_root_password -
openssl rand -base64 50 | docker secret create todobackend_secret_key -
docker secret ls
创建多个机密后,可以配置栈来使用这些机密:
version: '3.6'
networks:
...
volumes:
...
secrets:
todobackend_mysql_password:
external: true
todobackend_mysql_root_password:
external: true
todobackend_secret_key:
external: true
services:
app:
...
environment:
DJANGO_SETTINGS_MODULE: todobackend.settings_release
MYSQL_HOST: db
MYSQL_USER: todo
secrets:
- source: todobackend_mysql_password
target: MYSQL_PASSWORD
- source: todobackend_secret_key
target: SECRET_KEY
command:
...
db:
image: mysql:5.7
environment:
MYSQL_DATABASE: todobackend
MYSQL_USER: todo
MYSQL_PASSWORD_FILE: /run/secrets/mysql_password
MYSQL_ROOT_PASSWORD_FILE: /run/secrets/mysql_root_password
secrets:
- source: todobackend_mysql_password
target: mysql_password
- source: todobackend_mysql_root_password
target: mysql_root_password
...
具体配置步骤如下:
1. 声明顶级
secrets
参数,指定之前创建的每个机密的名称,并将每个机密配置为外部机密,因为这些机密是在栈外部创建的。
2. 重新配置
app
服务,通过
secrets
属性使用每个机密。指定目标为
MYSQL_PASSWORD
和
SECRET_KEY
,当将机密附加到服务时,会在
/run/secrets
创建基于内存的 tmpfs 挂载,每个机密存储在
/run/secrets/<target-name>
位置。
3. 配置
db
服务以使用 MySQL 密码和根密码机密,并配置每个机密的目标,使相应机密挂载到
db
服务容器中。
4. 从
db
服务中移除
MYSQL_PASSWORD
和
MYSQL_ROOT_PASSWORD
环境变量,并用基于文件的等效项替换,引用每个配置机密的路径。
部署更新后的栈后,可以通过以下命令确定 Swarm 管理器上运行的
app
服务实例的容器 ID,并检查
/run/secrets
目录的内容:
docker stack deploy --with-registry-auth -c stack.yml todobackend
docker ps -f name=todobackend -q
docker exec -it <container_id> ls -l /run/secrets
docker exec -it <container_id> cat /run/secrets/MYSQL_PASSWORD
若此时浏览外部负载均衡器 URL 的
/todos
路径,会收到访问被拒绝错误,这是因为
todobackend
应用不知道如何使用这些机密,需要对应用进行一些修改。
5. 配置应用以使用机密
之前使用入口点脚本在容器启动时注入机密,另一种更有效、更安全的方法是配置应用以原生支持机密管理策略。
对于 Docker 机密,由于机密挂载在容器本地文件系统的已知位置
/run/secrets
,配置应用支持 Docker 机密非常简单。以下是修改
src/todobackend/settings_release.py
文件以支持 Docker 机密的示例:
from .settings import *
import os
# Disable debug
DEBUG = True
# Looks up secret in following order:
# 1. /run/secret/<key>
# 2. Environment variable named <key>
# 3. Value of default or None if no default supplied
def secret(key, default=None):
root = os.environ.get('SECRETS_ROOT','/run/secrets')
path = os.path.join(root,key)
if os.path.isfile(path):
with open(path) as f:
return f.read().rstrip()
else:
return os.environ.get(key,default)
# Set secret key
SECRET_KEY = secret('SECRET_KEY', SECRET_KEY)
# Must be explicitly specified when Debug is disabled
ALLOWED_HOSTS = os.environ.get('ALLOWED_HOSTS', '*').split(',')
# Database settings
DATABASES = {
'default': {
'ENGINE': 'mysql.connector.django',
'NAME': os.environ.get('MYSQL_DATABASE','todobackend'),
'USER': os.environ.get('MYSQL_USER','todo'),
'PASSWORD': secret('MYSQL_PASSWORD','password'),
'HOST': os.environ.get('MYSQL_HOST','localhost'),
'PORT': os.environ.get('MYSQL_PORT','3306'),
},
'OPTIONS': {
'init_command': "SET sql_mode='STRICT_TRANS_TABLES'"
}
}
STATIC_ROOT = os.environ.get('STATIC_ROOT', '/public/static')
MEDIA_ROOT = os.environ.get('MEDIA_ROOT', '/public/media')
MIDDLEWARE.insert(0,'aws_xray_sdk.ext.django.middleware.XRayMiddleware')
首先创建一个名为
secret()
的简单函数,它接受设置或键的名称作为输入,以及一个可选的默认值。该函数会尝试在
/run/secrets
路径查找与请求的键同名的文件,如果找到则读取文件内容;否则,查找同名的环境变量;如果所有查找都失败,则返回传递给
secret()
函数的默认值。
更新
todobackend
应用以支持 Docker 机密后,需要提交更改并进行测试、构建和发布:
git commit -a -m "Add support for Docker secrets"
make login
make test
make release
make publish
发布镜像后,切换回连接到 Swarm 集群的终端会话,使用
docker stack deploy
命令重新部署栈:
docker stack deploy --with-registry-auth -c stack.yml todobackend
docker service ps todobackend_app --format "{{ .Name }}: {{ .CurrentState }}"
运行
docker service ps
命令时,可能会发现
todobackend
服务未重新部署,这是因为栈文件中默认使用最新镜像。为确保能够持续交付和部署应用,需要引用特定版本或构建标签。可以利用
todobackend
应用仓库中已有的 Makefile,并包含一个返回当前 Git 提交哈希的
APP_VERSION
环境变量,然后在栈文件中引用该变量:
version: '3.6'
services:
app:
image: 385605022855.dkr.ecr.us-east-1.amazonaws.com/docker-in-aws/todobackend:${APP_VERSION}
...
collectstatic:
image: 385605022855.dkr.ecr.us-east-1.amazonaws.com/docker-in-aws/todobackend:${APP_VERSION}
...
在 Makefile 根目录添加
deploy
配方:
.PHONY: test release clean version login logout publish deploy
export APP_VERSION ?= $(shell git rev-parse --short HEAD)
version:
@ echo '{"Version": "$(APP_VERSION)"}'
deploy: login
@ echo "Deploying version ${APP_VERSION}..."
docker stack deploy --with-registry-auth -c stack.yml todobackend
login:
$$(aws ecr get-login --no-include-email)
...
现在可以通过运行
make deploy
命令部署栈更新:
make deploy
docker service ps todobackend_app --format "{{ .Name }}: {{ .CurrentState }}"
由于栈现在配置了特定的镜像标签,检测到更改后会更新
app
服务。若此时浏览外部负载均衡器 URL 的
/todos
路径,认证错误应会被表不存在错误取代,这证明现在至少能够连接到数据库,但尚未处理 Docker Swarm 解决方案中的数据库迁移问题。
总结
本文详细介绍了在 AWS 中使用 Docker Swarm 进行容器编排与管理的一系列操作,包括部署栈、监控服务、创建持久存储、迁移 EBS 卷、使用 Docker 机密进行秘密管理以及配置应用以使用机密等。通过这些操作,可以实现更高效、安全的容器化应用部署和管理。
流程图
graph TD;
A[部署栈] --> B[监控服务];
B --> C[创建持久存储];
C --> D[迁移 EBS 卷];
D --> E[使用 Docker 机密管理];
E --> F[配置应用使用机密];
表格
| 操作步骤 | 命令示例 | 说明 |
|---|---|---|
| 部署栈 |
docker stack deploy --with-registry-auth -c stack.yml todobackend
| 创建网络和服务 |
| 监控服务 |
docker service ps todobackend_collectstatic
| 显示服务状态和历史 |
| 创建持久存储 |
docker stack deploy --with-registry-auth -c stack.yml todobackend
| 初始化数据卷 |
| 迁移 EBS 卷 |
docker stack deploy --with-registry-auth -c stack.yml todobackend
| 更改放置约束并观察迁移过程 |
| 创建 Docker 机密 |
openssl rand -base64 32 | docker secret create todobackend_mysql_password -
| 生成并创建机密 |
| 部署更新栈 |
docker stack deploy --with-registry-auth -c stack.yml todobackend
| 重新部署栈 |
在 AWS 中使用 Docker Swarm 进行容器编排与管理
6. 数据库迁移问题分析
当浏览外部负载均衡器 URL 的
/todos
路径时,出现表不存在错误,这表明虽然已经能够连接到数据库,但尚未处理数据库迁移问题。在 Docker Swarm 环境中,数据库迁移是确保应用正常运行的关键步骤。
7. 解决数据库迁移问题的思路
为了解决数据库迁移问题,需要在应用启动时执行数据库迁移脚本。可以通过修改
app
服务的启动命令来实现这一目标。以下是修改后的
stack.yml
文件示例:
version: '3.6'
networks:
net:
driver: overlay
volumes:
public:
driver: cloudstor:aws
driver_opts:
backing: shared
data:
driver: cloudstor:aws
driver_opts:
backing: relocatable
size: 10
ebstype: gp2
secrets:
todobackend_mysql_password:
external: true
todobackend_mysql_root_password:
external: true
todobackend_secret_key:
external: true
services:
app:
image: 385605022855.dkr.ecr.us-east-1.amazonaws.com/docker-in-aws/todobackend:${APP_VERSION}
ports:
- target: 8000
published: 80
networks:
- net
volumes:
- public:/public
environment:
DJANGO_SETTINGS_MODULE: todobackend.settings_release
MYSQL_HOST: db
MYSQL_USER: todo
secrets:
- source: todobackend_mysql_password
target: MYSQL_PASSWORD
- source: todobackend_secret_key
target: SECRET_KEY
command:
- sh
- -c
- |
python manage.py migrate
python manage.py runserver 0.0.0.0:8000
collectstatic:
image: 385605022855.dkr.ecr.us-east-1.amazonaws.com/docker-in-aws/todobackend:${APP_VERSION}
volumes:
- public:/public
db:
image: mysql:5.7
environment:
MYSQL_DATABASE: todobackend
MYSQL_USER: todo
MYSQL_PASSWORD_FILE: /run/secrets/mysql_password
MYSQL_ROOT_PASSWORD_FILE: /run/secrets/mysql_root_password
networks:
- net
volumes:
- data:/var/lib/mysql
command:
- --ignore-db-dir=lost+found
deploy:
replicas: 1
placement:
constraints:
- node.role == manager
具体修改内容如下:
1. 在
app
服务的
command
部分,添加了
python manage.py migrate
命令,用于执行数据库迁移。
2. 保留
python manage.py runserver 0.0.0.0:8000
命令,用于启动 Django 应用。
8. 重新部署栈并验证
修改
stack.yml
文件后,需要重新部署栈:
make deploy
部署完成后,可以通过以下命令验证服务状态:
docker service ps todobackend_app --format "{{ .Name }}: {{ .CurrentState }}"
若服务正常运行,再次浏览外部负载均衡器 URL 的
/todos
路径,表不存在错误应该会消失,应用应该能够正常显示数据。
9. 最佳实践总结
为了确保在 AWS 中使用 Docker Swarm 进行容器编排与管理的高效性和安全性,以下是一些最佳实践总结:
-
使用特定镜像标签
:避免在栈文件中使用默认的最新镜像,而是引用特定版本或构建标签,确保每次部署的一致性。
-
使用 Docker 机密管理
:使用 Docker 机密功能安全地存储和管理敏感信息,如数据库密码和应用密钥。
-
处理数据库迁移
:在应用启动时执行数据库迁移脚本,确保数据库结构与应用代码同步。
-
手动清理资源
:拆除栈时,手动删除栈中使用的任何卷,避免资源浪费。
流程图
graph TD;
A[发现数据库迁移问题] --> B[修改 app 服务启动命令];
B --> C[重新部署栈];
C --> D[验证服务状态];
D --> E{服务是否正常运行};
E -- 是 --> F[应用正常显示数据];
E -- 否 --> B;
表格
| 最佳实践 | 说明 |
|---|---|
| 使用特定镜像标签 | 确保每次部署的一致性,避免使用默认的最新镜像 |
| 使用 Docker 机密管理 | 安全地存储和管理敏感信息,如数据库密码和应用密钥 |
| 处理数据库迁移 | 在应用启动时执行数据库迁移脚本,确保数据库结构与应用代码同步 |
| 手动清理资源 | 拆除栈时,手动删除栈中使用的任何卷,避免资源浪费 |
总结
通过以上步骤,我们详细介绍了在 AWS 中使用 Docker Swarm 进行容器编排与管理的完整流程,包括部署栈、监控服务、创建持久存储、迁移 EBS 卷、使用 Docker 机密进行秘密管理、配置应用以使用机密以及处理数据库迁移等。遵循这些步骤和最佳实践,可以实现高效、安全的容器化应用部署和管理。
超级会员免费看
47

被折叠的 条评论
为什么被折叠?



