每天半小时,轻松学Docker第八篇:Docker应用部署实训案例

一、上期回顾
每天半小时,轻松学Docker第七篇:Docker对象查询与资源配额
二、案例 MariaDB服务部署
【实践01- MariaDB服务部署】
安装 MariaDB 服务。利用
mariadb:11.6.2-noble镜像创建并启动mariadb-studk容器,通过-v参数将宿主机目录绑定到容器的数据库存储目录,并设置容器重启策略。
1) 拉取 mariadb:11.6.2-noble 镜像
rz@demo:~$ docker pull mariadb:11.6.2-noble
11.6.2-noble: Pulling from library/mariadb
de44b265507a: Pull complete
...
Digest: sha256:a9547599cd87d7242435aea6fda22a9d83e2c06d16c658ef70d2868b3d3f6a80
Status: Downloaded newer image for mariadb:11.6.2-noble
docker.io/library/mariadb:11.6.2-noble
rz@demo:~$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
nginx 1.27-alpine 93f9c72967db 8 weeks ago 47MB
mariadb 11.6.2-noble 6722945a6940 2 months ago 407MB
mysql 8.0.40-debian 2d415917245b 3 months ago 610MB
hello-world latest d2c94e258dcb 21 months ago 13.3kB
2) 创建并启动 mariadb-studk 容器
rz@demo:~$ docker run -d --restart always --name mariadb-studk -v $HOME/data_mariadb:/var/lib/mysql -p 3310:3306 -e MARIADB_ROOT_PASSWORD=123456 mariadb:11.6.2-noble
67f61f0efddb313b89f80f592e2552ffe6bb73eb40534d00ae70a4cbc8f6f40f
rz@demo:~$ docker ps -a
CONTAINER ID IMAGE STATUS PORTS NAMES
67f61f0efddb mariadb:11.6.2-noble Up 3 seconds ...3310.. mariadb-studk
rz@demo:~$ ls $HOME/data_mariadb/
aria_log.00000001 ibdata1 multi-master.info tc.log
aria_log_control ib_logfile0 mysql undo001
ddl_recovery.log ibtmp1 performance_schema undo002
ib_buffer_pool mariadb_upgrade_info sys undo003
说明:
mariadb:11.6.2-noble镜像在创建容器时使用MARIADB_ROOT_PASSWORD环境变量设置数据库 root 用户的密码。
3) 验证容器自动重启策略
rz@demo:~$ sudo systemctl restart docker.service
rz@demo:~$ docker ps
CONTAINER ID IMAGE STATUS PORTS NAMES
67f61f0efddb mariadb:11.6.2-noble Up 2 minutes ...3310... mariadb-studk
验证结果:Docker 引擎重启后,
mariadb-studk容器自动启动,验证了--restart always策略生效。
4) 创建数据库并验证数据持久化
# 连接 MariaDB 服务
rz@demo:~$ mysql -h 127.0.0.1 -P3310 -uroot -p123456
Welcome to the MariaDB monitor. Commands end with ; or \g.
Your MariaDB connection id is 3
Server version: 11.6.2-MariaDB-ubu2404 mariadb.org binary distribution
...
# 创建测试数据库
MariaDB [(none)]> create database mariadb_test;
Query OK, 1 row affected (0.001 sec)
MariaDB [(none)]> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| mariadb_test |
| mysql |
| performance_schema |
| sys |
+--------------------+
5 rows in set (0.001 sec)
MariaDB [(none)]> exit;
Bye
# 在宿主机验证数据库目录
rz@demo:~$ ls $HOME/data_mariadb/
aria_log.00000001 ibdata1 mariadb_upgrade_info sys undo003
aria_log_control ib_logfile0 multi-master.info tc.log
ddl_recovery.log ibtmp1 mysql undo001
ib_buffer_pool mariadb_test performance_schema undo002
结果:在容器内创建的
mariadb_test数据库,在宿主机的绑定目录中成功生成了对应的目录。
5) 清理资源
# 停止并删除容器
rz@demo:~$ docker stop mariadb-studk
mariadb-studk
rz@demo:~$ docker rm mariadb-studk
mariadb-studk
# 删除数据目录
rz@demo:~$ sudo rm -rf $HOME/data_mariadb/
rz@demo:~$ ls
debian-src docker-ce scripts
关键要点总结:
-
使用
--restart always确保容器在 Docker 守护进程启动时自动重启 -
通过
-v参数实现宿主机目录与容器目录的绑定挂载 -
数据持久化:数据库文件存储在宿主机目录,容器删除后数据仍然保留
-
MariaDB 使用
MARIADB_ROOT_PASSWORD环境变量设置 root 密码
三、案例 Ubuntu20.04环境部署
【实践02-Ubuntu20.04环境部署】
在实际开发与运维工作中,软件开发与系统运维往往由不同的团队负责,甚至软件开发本身也由多个小组构成。不同的群体很难保证技术层面的一致性,会使用不同的开发环境、操作系统(如 CentOS、Debian、Redhat、SUSE、Ubuntu 等发行版)和版本。
这些由不同环境与技术开发出来的软件和程序,对实施运维的工程师造成了极大的挑战。使用容器技术可以达到环境隔离的效果,保障在开发、测试、运维与部署阶段使用一致的环境,避免环境差异造成的软件缺陷。
部署 Ubuntu 20.04 容器环境:
# 创建并以交互方式运行 ubuntu-20.04LTS 容器
root@debase:~# docker run --rm -it --name ubuntu-20.04LTS ubuntu:20.04
Unable to find image 'ubuntu:20.04' locally
20.04: Pulling from library/ubuntu
13b7e930469f: Pull complete
Digest: sha256:8feb4d8ca5354def3d8fce243717141ce31e2c428701f6682bd2fafe15388214
Status: Downloaded newer image for ubuntu:20.04
验证容器系统版本:
# 在容器内查看系统版本信息
root@81ffc633d788:/# cat /etc/os-release
NAME="Ubuntu"
VERSION="20.04.6 LTS (Focal Fossa)"
ID=ubuntu
ID_LIKE=debian
PRETTY_NAME="Ubuntu 20.04.6 LTS"
VERSION_ID="20.04"
HOME_URL="https://www.ubuntu.com/"
SUPPORT_URL="https://help.ubuntu.com/"
BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
VERSION_CODENAME=focal
UBUNTU_CODENAME=focal
# 退出容器
root@57eb0babf012:/# exit
exit
验证容器状态和镜像:
# 查看容器状态(已自动删除)
rz@demo:~$ docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
# 查看本地镜像
rz@demo:~$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
nginx 1.27-alpine 93f9c72967db 8 weeks ago 47MB
mariadb 11.6.2-noble 6722945a6940 2 months ago 407MB
mysql 8.0.40-debian 2d415917245b 3 months ago 610MB
hello-world latest d2c94e258dcb 21 months ago 13.3kB
ubuntu 20.04 b6f507652425 3 years ago 135MB
实践要点说明:
-
镜像拉取:由于本地没有
ubuntu:20.04镜像,使用docker run命令时会自动拉取镜像至本地 -
交互模式:使用
-it参数以交互方式进入容器,便于操作和调试 -
自动清理:使用
--rm参数,容器退出后自动删除,避免资源残留 -
环境验证:通过
/etc/os-release文件确认容器内系统为 Ubuntu 20.04.7 LTS -
环境一致性:通过容器化技术,可以确保在任何支持 Docker 的环境中都能获得完全一致的 Ubuntu 20.04 运行环境
四、案例 Nginx服务部署
【实践03-Nginx服务部署】
在实际的Web服务部署中,Nginx作为高性能的HTTP和反向代理服务器被广泛使用。通过容器化部署Nginx,可以快速搭建Web服务,并保持环境的一致性。
1) 拉取Nginx镜像
# 拉取官方Nginx镜像(默认最新版本)
rz@demo:~$ docker pull nginx:latest
latest: Pulling from library/nginx
...
Digest: sha256:aa8a62f5c81c2f4c5b55c63946caf4d6a2b1e2f5c5f5c5d5c5d5c5d5c5d5c5d
Status: Downloaded newer image for nginx:latest
docker.io/library/nginx:latest
# 查看已拉取的镜像
rz@demo:~$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
nginx latest 1f2bf2d6c5d3 2 weeks ago 187MB
mariadb 11.6.2-noble 6722945a6940 2 months ago 407MB
mysql 8.0.40-debian 2d415917245b 3 months ago 610MB
ubuntu 16.04 b6f507652425 3 years ago 135MB
2) 创建并运行Nginx容器
# 创建并启动nginx容器,映射端口80到宿主机的8080端口
rz@demo:~$ docker run -d --name nginx-web -p 8080:80 nginx:latest
c5d3e6f8a9b2c1d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8
# 查看容器运行状态
rz@demo:~$ docker ps
CONTAINER ID IMAGE COMMAND STATUS PORTS NAMES
c5d3e6f8a9b2 nginx:latest "nginx -g 'daemon off;'" Up 5 seconds 0.0.0.0:8080->80/tcp nginx-web
3) 验证Nginx服务
# 使用curl测试Nginx服务
rz@demo:~$ curl http://localhost:8080
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
...
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
...
</body>
</html>
# 或者在浏览器中访问 http://localhost:8080
4) 自定义Nginx配置(挂载自定义配置)
# 创建存放Nginx配置的目录
rz@demo:~$ mkdir -p ~/nginx-config
rz@demo:~$ mkdir -p ~/nginx-html
# 复制容器内的默认配置到宿主机(可选)
rz@demo:~$ docker cp nginx-web:/etc/nginx/nginx.conf ~/nginx-config/
rz@demo:~$ docker cp nginx-web:/usr/share/nginx/html/ ~/nginx-html/
# 停止并删除原有容器
rz@demo:~$ docker stop nginx-web
rz@demo:~$ docker rm nginx-web
# 使用自定义配置重新创建容器
rz@demo:~$ docker run -d --name nginx-web \
-p 8080:80 \
-v $HOME/nginx-html:/usr/share/nginx/html \
-v $HOME/nginx-config/nginx.conf:/etc/nginx/nginx.conf \
nginx:latest
5) 创建自定义首页
# 创建自定义HTML页面
rz@demo:~$ cat > ~/nginx-html/index.html << EOF
<!DOCTYPE html>
<html>
<head>
<title>自定义Nginx页面</title>
</head>
<body>
<h1>欢迎使用Docker部署的Nginx服务!</h1>
<p>当前时间: <span id="datetime"></span></p>
<script>
document.getElementById('datetime').textContent = new Date().toLocaleString();
</script>
</body>
</html>
EOF
# 重启容器使配置生效
rz@demo:~$ docker restart nginx-web
6) 验证自定义配置
# 再次访问验证自定义页面
rz@demo:~$ curl http://localhost:8080
<!DOCTYPE html>
<html>
<head>
<title>自定义Nginx页面</title>
</head>
<body>
<h1>欢迎使用Docker部署的Nginx服务!</h1>
<p>当前时间: <span id="datetime"></span></p>
<script>
document.getElementById('datetime').textContent = new Date().toLocaleString();
</script>
</body>
</html>
7) 设置容器自动重启
# 重新创建容器并设置自动重启策略
rz@demo:~$ docker stop nginx-web
rz@demo:~$ docker rm nginx-web
rz@demo:~$ docker run -d --name nginx-web \
--restart always \
-p 8080:80 \
-v $HOME/nginx-html:/usr/share/nginx/html \
-v $HOME/nginx-config/nginx.conf:/etc/nginx/nginx.conf \
nginx:latest
8) 验证自动重启策略
# 重启Docker服务,验证容器是否自动启动
rz@demo:~$ sudo systemctl restart docker
# 等待几秒后检查容器状态
rz@demo:~$ docker ps
CONTAINER ID IMAGE COMMAND STATUS PORTS NAMES
c5d3e6f8a9b2 nginx:latest "nginx -g 'daemon off;'" Up 10 seconds 0.0.0.0:8080->80/tcp nginx-web
9) 清理资源(可选)
# 停止并删除容器
rz@demo:~$ docker stop nginx-web
rz@demo:~$ docker rm nginx-web
# 删除自定义配置文件
rz@demo:~$ rm -rf ~/nginx-config ~/nginx-html
部署要点总结:
-
快速部署:使用官方Nginx镜像可快速搭建Web服务
-
端口映射:通过
-p参数将容器端口映射到宿主机 -
数据持久化:使用
-v参数挂载配置文件和网站目录 -
自动重启:设置
--restart always确保服务高可用 -
灵活配置:支持自定义配置和静态资源
-
环境一致:容器化部署确保开发、测试、生产环境一致性
五、案例 页面访问计数Python Web服务部署
【实践04-Python Web服务开发与部署】
开发Python Web服务,功能要求为:用户访问时显示服务器主机名、服务器IP地址、服务器当前时间、当前Web服务累计被访问次数,以及客户端的IP地址。通过容器化部署实现服务的高可用和数据持久化。
整体方案思路:
-
选择Python环境基础镜像
-
编写Python Web代码
-
创建自定义镜像
-
部署多个容器实例
-
验证自动重启策略
-
测试数据持久化
一、选择Python环境基础镜像
选择python:3.13-slim作为基础镜像,这是基于Debian系统构建的轻量级镜像。
root@debase:~# docker pull python:3.13-slim
3.13-slim: Pulling from library/python
8c7716127147: Pull complete
31fd2a94d723: Pull complete
66b685f2f76b: Pull complete
7d456e82f89b: Pull complete
Digest: sha256:5f55cdf0c5d9dc1a415637a5ccc4a9e18663ad203673173b8cda8f8dcacef689
Status: Downloaded newer image for python:3.13-slim
docker.io/library/python:3.13-slim
root@debase:~# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
python 3.13-slim 7b444340715d 7 weeks ago 117MB
二、编写Python Web代码
1) 创建项目目录
rz@demo:~$ mkdir $HOME/python-web
rz@demo:~$ ls
debian-src docker-ce python-web scripts
2) 编写Flask应用代码 ($HOME/python-web/app.py)
from flask import Flask, request, render_template_string
from datetime import datetime
import sqlite3, socket
app = Flask(__name__)
# SQLite数据库文件
DATABASE = '/root/sqlite/app.db'
# 初始化数据库和表
def init_db():
conn = sqlite3.connect(DATABASE)
cursor = conn.cursor()
cursor.execute('''
CREATE TABLE IF NOT EXISTS visits (
id INTEGER PRIMARY KEY AUTOINCREMENT,
count INTEGER NOT NULL DEFAULT 0
)
''')
# 如果表是空的,插入一个初始记录
cursor.execute('SELECT * FROM visits')
if cursor.fetchone() is None:
cursor.execute('INSERT INTO visits (count) VALUES (0)')
conn.commit()
conn.close()
# 获取当前的访问次数,并增加1
def get_and_increment_visit_count():
conn = sqlite3.connect(DATABASE)
cursor = conn.cursor()
cursor.execute('SELECT count FROM visits')
count = cursor.fetchone()[0]
cursor.execute('UPDATE visits SET count = ?', (count + 1,))
conn.commit()
conn.close()
return count + 1
# HTML模板
template = '''
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>Page Visit Counter</title>
</head>
<body>
<h1>The Server Name is: {{ server_hostname }}</h1>
<h1>The Server IP is: {{ server_ip }}</h1>
<h1>The current server time is: {{ server_time }}</h1>
<h1>This page has been visited {{ visit_count }} times.</h1>
<h1>Your IP Address is: {{ client_ip }}</h1>
</body>
</html>
'''
@app.route('/')
def index():
# 获取Web累计访问次数
visit_count = get_and_increment_visit_count()
# 获取访问者IP地址
client_ip = request.remote_addr
# 获取主机名
server_hostname = socket.gethostname()
# 获取服务器IP地址
server_ip = socket.gethostbyname(server_hostname)
# 获取当前服务器时间
server_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
return render_template_string(template,
server_hostname=server_hostname,
server_ip=server_ip,
server_time=server_time,
visit_count=visit_count,
client_ip=client_ip)
if __name__ == '__main__':
init_db() # 初始化数据库
app.run(debug=True, host='0.0.0.0', port=80)
三、创建自定义镜像
1) 创建临时测试容器
rz@demo:~$ docker run -it --name python-tmp -p 8080:80 python:3.13-slim /bin/bash
2) 在容器内创建目录结构
root@d4ade3e6816f:/# mkdir /root/sqlite
root@d4ade3e6816f:/# mkdir /root/python-web
3) 从宿主机拷贝文件到容器
rz@demo:~$ docker cp $HOME/python-web/app.py python-tmp:/root/python-web/
rz@demo:~$ docker cp /etc/apt/sources.list.d/ustc-debian.list python-tmp:/etc/apt/sources.list.d/debian.list
4) 在容器内安装依赖
root@d4ade3e6816f:/# apt update
root@d4ade3e6816f:/# apt install curl -y
root@d4ade3e6816f:/# pip install -i https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple Flask
5) 测试应用
root@d4ade3e6816f:/# python /root/python-web/app.py
6) 创建自定义镜像
rz@demo:~$ docker commit -a "demo" -m "Web Server" python-tmp python:count-ip-v1
rz@demo:~$ docker rm -f python-tmp
python-tmp
四、部署多容器实例
1) 创建数据目录
rz@demo:~$ mkdir $HOME/python-web/db1
rz@demo:~$ mkdir $HOME/python-web/db2
rz@demo:~$ tree $HOME/python-web/
/home/rz/python-web/
├── app.py
├── db1
└── db2
2) 创建并启动容器
# 启动第一个容器实例
rz@demo:~$ docker run -d --name python-web1 --restart always \
-p 8080:80 \
-v $HOME/python-web/db1/:/root/sqlite/ \
python:count-ip-v1 python /root/python-web/app.py
# 启动第二个容器实例
rz@demo:~$ docker run -d --name python-web2 --restart always \
-p 8082:80 \
-v $HOME/python-web/db2/:/root/sqlite/ \
python:count-ip-v1 python /root/python-web/app.py
rz@demo:~$ docker ps
CONTAINER ID IMAGE STATUS PORTS NAMES
d37a519d807b python:count-ip-v1 "python /root/python…" 3 seconds ago Up 2 seconds 0.0.0.0:8082->80/tcp, [::]:8082->80/tcp python-web2
2f205ba9da1d python:count-ip-v1 "python /root/python…" About a minute ago Up About a minute 0.0.0.0:8080->80/tcp, [::]:8080->80/tcp python-web1
3) 验证数据持久化
root@debase:/home/python-web# tree
.
├── app.py
├── db1
│ └── app.db
└── db2
└── app.db
3 directories, 3 files
五、验证服务功能
1) 测试服务访问
# 访问第一个容器
rz@demo:~$ curl http://localhost:8080
# 访问第二个容器
rz@demo:~$ curl http://localhost:8082

预期输出包含:
-
服务器主机名(容器ID)
-
服务器IP地址
-
当前服务器时间
-
访问次数统计
-
客户端IP地址
2) 验证自动重启策略
rz@demo:~$ sudo systemctl restart docker.service
rz@demo:~$ docker ps
CONTAINER ID IMAGE STATUS PORTS NAMES
0595845549fe python:count-ip-v1 Up 54 seconds ...8082->80... python-web2
d41d772175b6 python:count-ip-v1 Up 54 seconds ...8080->80... python-web1
六、验证数据持久化
1) 删除并重建容器
rz@demo:~$ docker stop python-web1 python-web2
rz@demo:~$ docker rm python-web1 python-web2
# 重新创建容器(使用相同的数据目录)
rz@demo:~$ docker run -d --name python-web1 --restart always \
-p 8080:80 \
-v $HOME/python-web/db1/:/root/sqlite/ \
python:count-ip-v1 python /root/python-web/app.py
2) 验证访问次数持续累计
rz@demo:~$ curl http://localhost:8080
# 访问次数应该从上次的数值继续累计
七、清理资源
# 停止并删除所有容器
rz@demo:~$ docker rm -f $(docker ps -qa)
# 删除数据目录
rz@demo:~$ rm -rf $HOME/python-web/db1/
rz@demo:~$ rm -rf $HOME/python-web/db2/
关键技术要点
-
环境一致性:通过容器化确保开发、测试、生产环境一致
-
数据持久化:使用绑定挂载实现数据库文件持久化存储
-
服务高可用:配置
--restart always确保服务自动恢复 -
多实例部署:通过不同端口和数据目录实现多实例隔离
-
资源隔离:每个容器实例拥有独立的数据和运行环境
六、总结与预告
1、下节课程预告
每天半小时,轻松学Docker第九篇:Dockerfile的镜像构建
更多推荐


所有评论(0)