Docker实战学习
企业实战
Docker-compose
Docker Compose来轻松高效的管理编排容器,定义运行多个容器
步骤:
- define your app's environment with a
Dockerfile
- Define the services that make up your app in
docker-compose.yml
- run
docker-compose up
启动,rundocker-compose down
停止
安装compose
# 1.
sudo curl -L "https://github.com/docker/compose/releases/download/1.29.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
# 2.
sudo chmod +x /usr/local/bin/docker-compose
# 3.
sudo ln -s /usr/local/bin/docker-compose /usr/bin/docker-compose
# 4.
docker-compose --version
compose 命令
1. docker-compose
-f --file docker-compose.yml # FILE指定Compose模板文件,默认为docker-compose.yml
2. docker-compose up # 启动所有服务
-d # 在后台运行服务器
-f docker-compose.yml # 指定compose.yml文件
3. docker-compose ps # 列出向项目中目前所有容器
4. docker-compose stop # 停止正在运行的容器
5. docker-compose down # 停止和删除容器,网络,
6. docker-compose logs # 查看服务容器的输出默认情况下,docker-compose将对不同的服务输出使用不同的颜色来区分。可以通过–no-color来关闭颜色
7. docker-compose build # 构建(重新构建)项目中的服务容器
8. docker-compose restart # 重启项目中的服务
9. docker-compose start # 启动已经存在的服务容器
10. docker-compose version # 打印version版本
实战练习(来自docker官网)
1.
mkdir composetest
cd composetest
2. app.py
"""
import time
import redis
from flask import Flask
app = Flask(__name__)
cache = redis.Redis(host='redis', port=6379) # redis为域名,对应hosts
def get_hit_count():
retries = 5
while True:
try:
return cache.incr('hits')
except redis.exceptions.ConnectionError as exc:
if retries == 0:
raise exc
retries -= 1
time.sleep(0.5)
@app.route('/')
def hello():
count = get_hit_count()
return 'Hello World! I have been seen {} times.\n'.format(count)
"""
3. 编写docker-compose.yml
4. 启动docker-compose up
5. 启动后 默认服务名为 文件名_服务名_num
例如:code_web_1 # code目录下的web服务,num副本数量
集群状态,副本数量不只有一个运行实例
默认规则
1. 网络规则
docke-compose 启动后,自动维护了一个网络 composetest_default (当前目录名称),服务间访问,不会使用ip,直接使用redis:6379
2. 如果在同一个网络下,可以直接使用域名访问
cache = redis.Redis(host='redis', port=6379)
yaml编写规则
# 只有三层
version: "3" # 版本
services: # 服务
web: # 服务1
image: redis # 镜像
build: /root/my_app # Dockerfile所在目录
command: pipenv run python app.py # 执行命令
container_name: my_web # 容器名
depends_on: # 启动顺序在redis和mysql之后
- redis
- mysql
ports: # 端口映射,暴露容器端口到主机的任意端口或指定端口(类似docker run -p)
- "3000"
- "8000:8000"
- "127.0.0.1:8001:7801"
expose: # 端口映射,暴露容器端口到其他容器,不暴露给主机(类似docker run --link)
- "6379"
volumes: # 挂载一个存在的数据卷容器
- /var/lib/mysql # Docker 会自动在创建一个数据卷,匿名挂载
- /opt/data:/var/lib/mysql # 绝对路径挂载
- datavolume:/var/lib/mysql # 具名挂载
volumes_from: # 从另一个服务或容器挂载其数据卷
- redis
entrypoint: /entrypoint.sh
environments:
- "MYSQL_ROOT_PASSWORD=123"
links: # 不推荐吧,在web的/etc/hosts里配置db的ip, 例如: 192.168.10.1 db
- db
- myserver:db # 在容器web中,容器myserver的名字就是db, 可以连接数据库 http://db:6379
redis: # 服务2
mysql: # 服务3
volumes: # 全局配置
networks:
configs:
实例:
version: "3.9"
services:
web:
container_name: my_web
build: .
ports: # 暴露端口
- "5000:5000"
depends_on: # web依赖于db和redis, 启动顺序先启动db和redis最后启动web
- db
- redis
command: pipenv run flask run
environment:
- a=b
volumes:
- ./data:/var/xxx
redis:
image: "redis:alpine"
db:
image: mysql
Docker Swarm
集群方式的部署,四台服务器,我自己用virtualbox + centos7 * 4
节点:
- 管理节点(manager):操作都在manager节点上
- 工作节点
搭建集群和命令
-
docker swarm --help
-
私网和公网
-
对node1 机器执行
docker swarm init --advertise-addr 私网地址 --listen-addr 私网地址
例如:
$ docker swarm init --advertise-addr # 初始化节点 添加--advertise-addr参数的原因是大多数情况下我们的主机都不只有一张网卡。而一个swarm集群需要辨明集群所在的子网络是哪张网卡的 --listen-addr指出的是这个集群暴露给外界调用的HTTPAPI的socket地址 """ To add a worker to this swarm, run the following command: docker swarm join --token SWMTKN-1-56f5ccl2kyls04d5uxx1wwva9rv9ijtt4zs8jwcvxroxstf0s6-d5k6pk82emis2rpla7qevqe60 192.168.0.115:2377 # 复制到其他机器执行,可以加入为worker节点 To add a manager to this swarm, run 'docker swarm join-token manager' and follow the instructions. """ # 初始化节点 docker swarm init # 生成一个加入manager或worker的token docker swarm join-token manager docker swarm join-token worker """ To add a worker to this swarm, run the following command: docker swarm join \ --token SWMTKN-1-49nj1cmql0jkz5s954yi3oex3nedyz0fb0xx14ie39trti4wxv-8vxv8rssmk743ojnwacrr2e7c \ 192.168.99.100:2377 """ # 查看节点(只有manger节点可以使用, 比如我现在四台虚拟机,则node ls为四个) docker node ls """ ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION ruyjls193xrvtdvt4h9b8020l * master Ready Active Leader 20.10.6 mg6qegsok9i19spfo8el7ec9j node2 Ready Active 20.10.6 48fstkzfi627kze80eqd60tbc node3 Ready Active Reachable 20.10.6 aikez4xzcvy9602hjxhynxnaa node4 Ready Active 20.10.6 """ # 然后node1和node3位manger, node2 和node4位worker # 在manager node 中创建服务, docker service create --replicas 1 --name helloworld alpine ping docker.com # 查看服务 docker service ls # 查看服务详细信息 docker service inspect --pretty helloworld # 查看那些node正在运行这个service, 一旦我们在swarm中部署了service,我们就可以通过,这个命令service的container的数量。我们将这些container称之为task。 docker service ps helloworld # 对service扩缩容 docker service scale 服务名(或id)=个数 例如: docker service scale helloworld=3 # 对删除service docker service rm helloworld # docker ps 中对应的容器一会也会删除 # 更新服务,滚动式更新(例如redis:3.0.6,现在你想升级为redis3.0.7) docker service update --image redis:3.0.7 redis # 修改一个node的Availability可用性drain。 docker node update --availability drain <NODE-ID> # DRAIN availability prevents a node from receiving new tasks from the swarm manager. 则这个node不会接收来自manager的tasks,manager会结束之前drain node的tasks,会重新在active的node上创建tasks 例如:docker node update --availability drain master # 暴露端口 # 使用 --publish 参数来暴露端口。target用来指定container内部的端口号; # published用来指定向外暴露的端口号,这个端口号将被绑定到路由网上。 # 如果不设置published将会为service的task指定一个随机的端口号,需要查看task的信息才能确定这个端口号是多少。 # 也可以使用-p 8080:80 docker service create --name 服务名 --publish published=<PUBLISHED-PORT>,target=<CONTAINER-PORT> 镜像 例如: docker service create --name my_web --replicas 3 --publish published=8080,target=80 nginx
遇到的问题
[root@localhost ~]# docker swarm join --token SWMTKN-1-0hv0so8y1bl4hp9ahzand12rp82c8afrea6m7n22ae9gktx8bf-2oh0ty4vecnfm3tbin515hz30 192.168.0.115:2377
"""
Error response from daemon: rpc error: code = Unavailable desc = connection error: desc = "transport: Error while dialing dial tcp 192.168.0.115:2377: connect: no route to host"
"""
# manager机器的防火墙导致
systemctl status firewalld.service # 查看防火墙状态
systemctl stop firewalld.service # 停止防火墙
systemctl disable firewalld.service # 永久停止
Raft协议(需深入了解)
双主双从:假设一个节点挂了,其他节点是否可用?
实验:
-
将docker1的机器docker服务停止,宕机,双主,另外一个主节点也不能使用
-
可以将其他节点离开,则显示
Down
的状态 -
当三个机器为管理节点时候,有一个宕机,其余两个仍然可用。如果有两个为管理节点,一个宕机,另一个不能用了。
总结:
- 集群要保证高可用,至少3个机器为管理节点
实战: 动态扩缩容
# 1. 创建服务
docker service create -p 8888:80 --name my_nginx nginx
# 2. 调整为3个副本,动态扩缩容
docker service update --replicas 3 my_nginx
docker service scale my_nginx=3 # 法二
# 3. 访问服务
curl 192.168.0.116:8888
概念总结
swarm: 集群的管理和编号,docker可以初始化一个swarm集群,其他节点可以加入(管理者,工人)。
**Node: ** 对应的docker节点,多个节点组成一个网络集群(管理,工作者)
Service: 任务,可以在管理节点或者工作节点来运行,核心。
**Task: ** 容器内的命令,细节任务
其他命令:
--mode string # 创建的服务,以什么模式运行 Service mode (replicated, global, replicated-job, or global-job)
docker service create --mode replicated --name my_nginx nginx
docker service create --mode gobal --name my_nginx nginx # 每个node都有nginx
docker swarm 网络(需深入了解)
在 Swarm Service 中有三个重要的网络概念:
- Overlay networks 管理 Swarm 中 Docker 守护进程间的通信。你可以将服务附加到一个或多个已存在的
overlay
网络上,使得服务与服务之间能够通信。 - ingress network 是一个特殊的
overlay
网络,【用于服务节点间的负载均衡】。当任何 Swarm 节点在发布的端口上接收到请求时,它将该请求交给一个名为IPVS
的模块。IPVS
跟踪参与该服务的所有IP地址,选择其中的一个,并通过ingress
网络将请求路由到它。
初始化或加入 Swarm 集群时会自动创建ingress
网络,大多数情况下,用户不需要自定义配置,但是 docker 17.05 和更高版本允许你自定义。 - docker_gwbridge是一种桥接网络,将
overlay
网络(包括ingress
网络)连接到一个单独的 Docker 守护进程的物理网络。默认情况下,服务正在运行的每个容器都连接到本地 Docker 守护进程主机的docker_gwbridge
网络。
docker_gwbridge
网络在初始化或加入 Swarm 时自动创建。大多数情况下,用户不需要自定义配置,但是 Docker 允许自定义。

docker swarm + nfs 持久化存储实战
# 1. 管理节点
# 需要:安装nfs服务端、配置nfs主配置文件、添加权限、启动
yum install nfs-utils -y
vim /etc/exports
# 2) 添加目录给任意网段访问并添加读写权限
/nfs *(rw,no_root_squash,sync)
# 3) 创建共享目录,添加权限
mkdir /nfs
chmod 777 /nfs
# 4) 开启rpc服务,启动服务并设置开机自启
systemctl start rpcbind
systemctl start nfs
systemctl enable rpcbind
systemctl enable nfs
# 2. 工作节点
# 需要:安装nfs客户端、启动服务
yum install nfs-utils -y
# 启动服务
systemctl start rpcbind # 开启rpc服务
systemctl start nfs
# 3. 创建服务
docker service create --replicas 3 --mount 'type=volume,src=nfs-test,dst=/usr/share/nginx/html,volume-driver=local,volume-opt=type=nfs,volume-opt=device=192.168.0.116:/nfs,"volume-opt=o=addr=192.168.0.116,vers=4,soft,timeo=180,bg,tcp,rw"' -p 8888:80 --name nginx nginx:1.12
"""
type=volume, # 存储类型
src=<VOLUME-NAME>, # volume名称,
dst=<CONTAINER-PATH>, # 目标路径, 容器内路径
volume-driver=local, # 官方配置
volumeopt=type=nfs, # 服务类型
volume-opt=device=<nfs-server>:<nfs-path>, # 官方配置参数, nfs路径
"volume-opt=o=addr=<nfsaddress>,vers=4,soft,timeo=180,bg,tcp,rw"
"""
docker service ps nginx
# 4. 查看volumes
docker volume inspect nfs-test
# 5. 测试数据同步
# 1) 在/nfs目录下创建 aaa.py文件
touch aaa.py
# 2) 在其他node下,查看
cd /var/lib/docker/volumes/nfs-test/_data
ls
# 3) 进入容器节点查看容器是否同步,在容器外node查看
docker exec -it 44d9f66cb84e /bin/bash
cd /usr/share/nginx/html
ls
CI/CD之jenkins
参考:
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通