- Docker安装
- Docker的镜像和容器
- Docker的网络
- Docker的持久化存储和数据共享
- Docker Compose多容器部署
- 容器编排Docker Swarm
- DevOps初体验—Docker Cloud和Docker企业版
- 容器编排Kubernetes
- 容器的的运维和监控
- Docker+DevOps实战—过程和工具
安装
Mac
下载并安装即可:https://download.docker.com/mac/stable/Docker.dmg
1 2 3 4 5 |
# 查看信息的一些命令 docker --version docker version docker info # 还可安装Kitematic进行可视化的安装 |
Mac/Windows下安装虚机的小技巧:Vagrant-首先要安装VirtualBox,然后在Vagrant下载页面进行软件的下载
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
vagrant --help vagrant version # 快速创建虚拟机 mkdir -p vagrant/centos cd vagrant/centos/ # 初始化Vagrantfile vagrant init centos/7 # 安装 vagrant up # 通过ssh进入 vagrant ssh # 查看状态 vagrant status # 停止服务 vagrant halt # 删除 vagrant destroy # 因网络问题,有时需要通过迅雷等软件先把文件下载下来,再进行本地安装 # 本地安装box,以Windows 10为例 vagrant box add Microsoft/EdgeOnWindows10 /path/to/virtualbox.box vagrant init Microsoft/EdgeOnWindows10 vagrant up # 安装 scp 插件,然后可通过 vagrant scp 命令拷贝本地文件或目录到主机上 vagrant plugin install vagrant-scp |
搜索相关操作系统的Vagrantfile:https://app.vagrantup.com/boxes/search,找到相应文件后创建一个名为Vagrantfile的文件或直接根据命令初始化,再使用vagrant up进行安装
以CentOS安装为例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
# 卸载旧版本 sudo yum remove docker docker-client docker-client-latest docker-common docker-latest \ docker-latest-logrotate docker-logrotate docker-selinux docker-engine-selinux docker-engine # 安装requirements sudo yum install -y yum-utils device-mapper-persistent-data lvm2 # 添加repo sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo # 安装docker sudo yum install docker-ce -y # 启动docker sudo systemctl start docker sudo systemctl enable docker # 运行Hello World sudo docker run hello-world |
启动 Vagrant 自动安装 Docker,修改 Vagrantfile 末尾为
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
config.vm.provision "shell", inline: <<-SHELL sudo yum remove docker docker-common docker-selinux docker-engine sudo yum install -y 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 -y docker-ce sudo systemctl start docker SHELL end # 其它 # 设置桥接网络 config.vm.network "public_network", :bridge => 'en0: Wi-Fi (AirPort)' #设置私有 IP config.vm.network "public_network", ip: "192.168.1.120" |
Docker Machine
Windows 和 Mac 安装时会默认带有 Docker Machine, Linux 下安装参见:https://github.com/docker/machine/releases/
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
# 创建一个名为 demo 的虚机 docker-machine create demo # 显示虚拟机列表 docker-machine ls # SSH 连接 demo docker-machine ssh demo # 启动 demo docker-machine start demo # 关闭 demo docker-machine stop demo # 删除 demo docker-machine rm demo # 本地执行 docker-machine env demo eval $(docker-machine env demo) # unset docker-machine env --unset eval $(docker-machine env --unset) # 通过添加 vagrant 用户到 docker 组,在执行 docker 命令时不再 需要添加 sudo sudo gpasswd -a vagrant docker https://github.com/AliyunContainerService/docker-machine-driver-aliyunecs 把文件重命名为:/usr/bin/docker-machine-driver-aliyunecs # 验证安装是否成功 docker-machine create -d aliyunecs --help # 使用 Docker Machine 在阿里云上创建 Host docker-machine create -d aliyunecs --aliyunecs-io-optimized=optimized --aliyunecs-instance-type=ecs.c4.large --aliyunecs-access-key-id=xxx --aliyunecs-access-key-secret=xxx --aliyunecs-region=cn-hangzhou demo |
此外还可通过 Docker Playground 来进行测试,但经测试国内速度较慢且经常会空间不足无法创建,以上方法中推荐 Vagrant,灵活性较 Docker Machine 更强。
Docker 命令
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
# Dockerfile from hub.c.163.com/library/tomcat MAINTAINER Alan alan@xxx.com COPY demo.war /usr/local/tomcat/webapps # Build,demo:latest 分别为名称和标签 docker build -t demo:latest . # 指定端口并运行 -d 后台运行 -p 指定端口 docker run -d -p 8888:8080 demo # 交互式运行 Container docker run -it centos # 以 /bin/bash 进入镜像交互式环境 docker run -it <IMAGE ID> /bin/bash docker pull hub.c.163.com/library/tomcat:latest # 查看容器名等信息 docker ps # 进入容器 docker exec -it 12e1b6c023e bash # 拷贝到本地 docker cp <container id>:/demo.war ./ docker inspect <container id> # 安装 Wordpress(docker-compose.yml) docker-compose build docker-compose up # 停止当前所有运行的 Docker docker stop $(docker ps -a -q) # 查看镜像 docker images docker image ls # 删除镜像,-f 为强制删除 docker rmi -f <image id> #查看容器 docker container ls # 运行中的容器 docker container ls -a # 列出所有容器,与 docker ps -a相同 # 删除容器 docker container rm <container id> # 列出所有容器 id,与docker container ls -a | awk {'print $1'}相似 docker container ls -aq # 删除所有容器 docker rm $(docker container ls -aq) # 或docker rm $(docker ps -aq) # 删除已退出容器 docker rm $(docker container ls -f "status=exited" -q) #容器运行日志 docker logs docker logs -f -t --tail 10 <CONTAINER ID> |
Docker的镜像和容器
- 后台进程(dockerd)
- REST API Server
- CLI接口(docker)
底层技术支持
- Namespaces:做隔离 pid, net, ipc, mnt, uts
- Control groups:做资源限制
- Union file systems:Container 和 image 的分层
Docker 镜像
1 2 3 4 5 6 7 8 9 10 11 12 13 |
# 1、手写 Dockerfile 自行 build FROM ubuntu:14.04 LABEL maintainer="Alan Hou <xxx@gmail.com>" RUN apt-get update && apt-get install -y redis-server EXPOSE 6379 ENTRYPOINT ["/usr/bin/redis-server"] # build docker build -t ubuntu-14:latest . # 2、docker pull sudo docker pull ubuntu:14.04 # sudo docker images 进行查看 |
什么是 Image?
- 文件和 meta data 的集合(root filesystem)
- 分层的,并且每一层都可以添加改变删除文件,成为一个新的 image
- 不同的 image 可以共享相同的 layer
- Image 本身是 read-only 的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
# 执行 hello-world docker pull hello-world docker run hello-world # 手写 base image hello-world mkdir hello-world cd hello-world/ vi hello.c #include<stdio.h> int main() { printf("hello docker!\n"); } sudo yum install gcc glibc glibc-static -y gcc -static hello.c -o hello # 创建 Dockerfile vi Dockerfile FROM scratch ADD hello / CMD ["/hello"] # build 我们的 base image docker build -t alanhou/hello-world . # 此时执行docker image ls就可以看到alanhou/hello-world # 运行 docker run alanhou/hello-world |
什么是 Container?
- 通过 Image 创建(copy)
- 在 Image layer 之上建立一个 container layer(可读写)
- 类比面向对象:类(Image)和实例(Container)
- Image负责 app 的存储和分发,Container 负责运行 app
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
# container commit 为 image docker container commit/docker commit # 示例:(把拉下来的 CentOS 进行修改后生成 Image,不推荐) docker commit musing_aryabhata alanhou/centos-alan docker image build/docker build # 示例 vi Dockerfile FROM centos RUN yum install -y vim docker build -t alanhou/centos-vim . |
Dockerfile
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
FROM 指定 base image FROM scratch # 从0开始 # 选择其它 base image FROM centos FROM ubuntu:14.04 LABEL 指定 Metadata LABEL maintainer="xxx@gmail.com" LABEL version="1.0" LABEL description="Demo description" RUN执行命令 RUN yum update && yum install -y vim python-dev RUN apt-get update && apt-get install -y perl \ pwgen --no-install-recommends && rm -rf \ /var/lib/apt/lists/* RUN /bin/bash -c 'source $HOME/.bashrc; echo $HOME' WORKDIR 设定当前工作目录 WORKDIR /test # 如果没有会自动创建 WORKDIR demo RUN pwd ADD and COPY把本地文件添加到 image 中 ADD hello / ADD test.tar.gz / # 添加到根目录并解压 WORKDIR /root ADD hello test/ # /root/test/hello WORKDIR /root COPY hello test/ ENV ENV MYSQL_VERSION 5.6 # 设置常量 RUN apt-get install -y mysql-server= "${MYSQL_VERSION}" \ #引用常量 && rm -rf /var/lib/apt/lists/* VOLUMN and EXPOSE 存储和网络 CMD and ENTRYPOINT # RUN:执行命令并创建新的 Image Layer # CMD:设置容器启动后默认执行的命令和参数;如果 docker run 指定了其它命令,CMD命令被忽略;如果定义了多个 CMD,只有最后一个会执行 # ENTRYPOINT:设置容器启动时运行的命令;不会被忽略,一定会执行;最佳实践:写一个 shell 脚本作为 entrypoint # Shell 格式 RUN apt-get install -y vim CMD echo "hello docker" ENTRYPOINT echo "hello docker" # Exec 格式 RUN ["apt-get", "install", "-y", "vim"] CMD ["/bin/echo", "hello docker"] ENTRYPOINT ["/bin/echo", "hello docker"] |
Docker 官方 Image 文件:https://github.com/docker-library
Docker 官方文档:https://docs.docker.com/engine/reference/builder/
Docker镜像的发布
1 2 |
docker login docker image push/docker push <REPOSITORY> # 如alanhou/hello-world |
方法二(推荐)Create Automated Build,关联 GitHub,会自动根据 Dockerfile 自动生成镜像
1 |
sudo docker run -d -p 5000:5000 --restart always --name registry registry:2 |
默认 push 私有服务器不被信任(Get https://192.168.1.6:5000/v2/: http: server gave HTTP response to HTTPS client)
1 2 3 4 5 6 7 8 9 10 11 |
docker build -t xxx.xxx.xxx.xxx:5000/hello-world . vi /etc/docker/daemon.json {"insecure-registries":["xxx.xxx.xxx.xxx:5000"]} vi /lib/systemd/system/docker.service EnvironmentFile = /etc/docker/daemon.json sudo systemctl daemon-reload sudo systemctl reload docker docker push xxx.xxx.xxx.xxx:5000/hello-world |
Docker Registry Api 可用于查看验证 push 是否成功,如通过 http://your.ip.address:5000/v2/_catalog查看仓库,得到如下结果:
1 2 3 4 5 |
{ repositories: [ "hello-world" ] } |
示例文件(制作基于 Flask Hello World 程序的镜像)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
# Dockerfile FROM python:2.7 LABEL maintainer="Alan Hou<xxx@gmail.com>" RUN pip install flask COPY app.py /app/ WORKDIR /app EXPOSE 5000 CMD ["python", "app.py"] docker build -t alanhou/flask-hello-world . # 对于中间状态的镜像可以通过/bin/bash 查看环境中存在的问题 docker run -it bacce0142ae /bin/bash # 运行 docker run -d alanhou/flask-hello-world # 进入运行中的容器 docker exec -it <CONTAINER ID> /bin/bash |
示例文件2(带参数执行的 Docker Image)
1 2 3 4 5 6 7 8 9 10 |
# Dockerfile FROM ubuntu RUN apt-get update && apt-get install -y stress ENTRYPOINT ["/usr/bin/stress"] CMD [] # 创建镜像 docker build -t alanhou/ubuntu-stress . # 运行 docker run -it alanhou/ubuntu-stress --vm 1 |
Docker的网络
网络是基于数据包的通信方式,网络的分层
Ping(ICMP协议)用于验证 IP 的可达性(防火墙可能会禁止 Ping 操作);telnet 用于检查服务的可用性(telnet ip.addr port)
Network Namespace 的小测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
# 添加网络命名空间 sudo ip netns add test1 sudo ip netns add test2 # 查看 sudo ip netns list # 设置关联 sudo ip link add veth-test1 type veth peer name veth-test2 sudo ip link set veth-test1 netns test1 sudo ip link set veth-test2 netns test2 # 设置 IP sudo ip netns exec test1 ip addr add 192.168.1.1/24 dev veth-test1 sudo ip netns exec test2 ip addr add 192.168.1.2/24 dev veth-test2 # 启动 sudo ip netns exec test1 ip link set dev veth-test1 up sudo ip netns exec test2 ip link set dev veth-test2 up # 查看IP 地址 sudo ip netns exec test1 ip a sudo ip netns exec test2 ip a # Ping 测试 sudo ip netns exec test2 ping 192.168.1.1 |
Docker 默认网络Bridge
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
# 启动容器 test1 docker run -d --name test1 busybox /bin/sh -c "while true; do sleep 3600; done" # 查看网络并显示NETWORK ID docker network ls docker network inspect <NETWORK ID> # 安装并查看桥接网络 sudo yum install -y bridge-utils brctl show # 启动容器 test2 docker run -d --name test2 busybox /bin/sh -c "while true; do sleep 3600; done" docker network inspect bridge # Docker Link docker stop test2 docker rm test2 # 重新启动 test2 docker run -d --name test2 --link test1 busybox /bin/sh -c "while true; do sleep 3600; done" docker exec -it test2 /bin/sh # 在交互命令中除 ip 外还可通过 ping test1来测试连接 # 创建网络(通过-d 指定 driver) docker network create -d bridge my-bridge # 创建容器并指定网络 docker run -d --name test3 --network my-bridge busybox /bin/sh -c "while true; do sleep 3600; done" # 连接已有容器到指定网络 docker network connect my-bridge test2 # 注:连接到自定网络中的容器默认相互 link,所以在 test2中通过 ping test3是可以连接的 # 端口映射 -p 为Port Map docker run --name web -d -p 80:80 nginx |
容器网络 none & host
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
docker run -d --name test1 --network none busybox /bin/sh -c "while true; do sleep 3600; done" # 查看 none网络 docker network inspect none # 进入容器,通过 ip a 命令会发现仅存在本地回环地址 lo,即局域网内无法访问 docker exec -it test1 /bin/sh # 删除 docker stop test1 && docker rm test1 # 创建并设置 test1网络为 host docker run -d --name test1 --network host busybox /bin/sh -c "while true; do sleep 3600; done" # 查看 host网络 docker network inspect host # 进入容器,通过 ip a可发现与宿主机相同,所以若启动多个服务(如 Nginx)可能会出现端口冲突的问题 docker exec -it test /bin/sh |
# 更复杂的 Docker 测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
sudo docker run -d --name redis redis docker build -t alanhou/flask-redis . # -e 设置环境变量 docker run -d -p 5000:5000 --link redis --name flask-redis -e REDIS_HOST=redis alanhou/flask-redis # app.py from flask import Flask from redis import Redis import os import socket app = Flask(__name__) redis = Redis(host=os.environ.get('REDIS_HOST', '127.0.0.1'), port=6379) @app.route('/') def hello(): redis.incr('hits') return 'Hello Container World! I have been seen %s times and my hostname is %s.\n' % (redis.get('hits'),socket.gethostname()) if __name__ == "__main__": app.run(host="0.0.0.0", port=5000, debug=True) # Dockerfile FROM python:2.7 LABEL maintaner="Alan Hou xxx@gmail.com" COPY . /app/ WORKDIR /app RUN pip install flask redis EXPOSE 5000 CMD [ "python", "app.py" ] |
Overlay 网络和多机容器间通信
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
# 在两台宿主机上分别执行 # 配置 etcd(分布式的 key,value 工具,用于管理并防止 IP 冲突) wget https://github.com/coreos/etcd/releases/download/v3.0.12/etcd-v3.0.12-linux-amd64.tar.gz tar zxvf etcd-v3.0.12-linux-amd64.tar.gz cd etcd-v3.0.12-linux-amd64 # your.ip.addr为本机 IP,dest.ip.addr为需连接的服务器 IP, docker-node1,2可根据实际情况修改为其它名称 nohup ./etcd --name docker-node1 --initial-advertise-peer-urls http://your.ip.addr:2380 \ --listen-peer-urls http://your.ip.addr:2380 \ --listen-client-urls http://your.ip.addr:2379,http://127.0.0.1:2379 \ --advertise-client-urls http://your.ip.addr:2379 \ --initial-cluster-token etcd-cluster \ --initial-cluster docker-node1=http://your.ip.addr:2380,docker-node2=http://dest.ip.addr:2380 \ --initial-cluster-state new& # 查看 etcd 服务状况 ./etcdctl cluster-health # 重新启动 docker 服务 sudo service docker stop sudo /usr/bin/dockerd -H tcp://0.0.0.0:2375 -H unix:///var/run/docker.sock --cluster-store=etcd://your.ip.add:2379 --cluster-advertise=your.ip.addr:2375& # 创建 overlay 网络(另一台机器会自动创建 docker network ls) sudo docker network create -d overlay demo # 在 A 主机上创建 docker run -d --name test1 busybox /bin/sh -c "while true; do sleep 3600; done" # 在 B 主机上创建 docker run -d --name test2 busybox /bin/sh -c "while true; do sleep 3600; done" # 此时会发现两个窗口之间可相互 ping 通,即实现了通信,如 docker exec -it test1 ping test2 |
Docker的持久化存储和数据共享
Docker 持久化数据的方案
- 基于本地文件系统的 Volume:Data Volume,docker create 或 docker run 时添加-v 参数
- 基于 plugin 的 Volume:第三方存储方案,如 NAS, AWS
以下为本地文件系统持久化的两种操作方法:
Data Volume
可通过 Dockerfile中的 VOLUME进行指定,如 MySQL的官方 Dockerfile 中使用VOLUME /var/lib/mysql
1 2 3 4 5 6 7 8 |
sudo docker run -d --name mysql1 -e MYSQL_ALLOW_EMPTY_PASSWORD=true mysql sudo docker volume ls sudo docker volume inspect <VOLUME NAME> # 可以看到/var/lib/docker/volumes/下有存储相关目录,即使删除容器也不影响,如需删除volume,需手动执行 sudo docker volume rm <VOLUME NAME> # 默认生成的<VOLUME NAME>太长,可以通过-v 参数指定,如: sudo docker run -d -v mysql:/var/lib/mysql --name mysql1 -e MYSQL_ALLOW_EMPTY_PASSWORD=true mysql # 会发现在我们删除容器后,重新使用相同的-v 参数原容器创建的数据库等信息依然存在 |
Bind Mounting
本地文件和容器之前的一个映射关系(/home/aaa 为本地,可使用$(pwd)映射本地目录)
1 |
docker run -v /home/aaa:/root/aaa |
Docker Compose多容器部署
安装部署 WordPress
1 2 3 4 |
# 安装 MySQL容器 docker run -d --name mysql -v mysql-data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=root -e MYSQL_DATABASE=wordpress mysql:5.7 # 安装 Wordpress容器 docker run -d -e WORDPRESS_DB_HOST=mysql:3306 --link mysql -p 80:80 wordpress |
Docker Compose是一个可以通过一个 yml 文件定义多容器 Docker应用的工具,默认名称 docker-compose.yml
services:一个 service 代表一个 container,启动类似 docker run
Windows, Mac 默认已安装 Docker Compose,Linux
1 2 3 4 |
sudo curl -L "https://github.com/docker/compose/releases/download/1.23.1/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose sudo chmod +x /usr/local/bin/docker-compose # 查看安装状况 docker-compose --version |
以上述 WordPress 部署为例
docker-compose.yml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
version: '3' services: wordpress: image: wordpress ports: - 80:80 environment: WORDPRESS_DB_HOST: mysql WORDPRESS_DB_PASSWORD: root networks: - my-bridge mysql: image: mysql:5.7 environment: MYSQL_ROOT_PASSWORD: root MYSQL_DATABASE: wordpress volumes: - mysql-data:/var/lib/mysql networks: - my-bridge volumes: mysql-data: networks: my-bridge: driver: bridge |
执行
1 2 3 |
docker-compose up -d # 或指定 yml 文件 docker-compose -f docker-compose.yml up -d |
其它命令
1 2 3 4 5 6 7 8 |
# 停止服务 docker-compose stop # 停止并删除 docker-compose down # 查看 docker-compose ps docker-compose images docker-compose exec ... |
水平扩展和负载均衡
1 2 3 4 5 6 7 8 9 10 11 |
# 修改前述docker-compose.yml 文件:删除 wordpress 下的 ports 部分,并在 services 下添加 lb: image: dockercloud/haproxy links: - wordpress ports: - 80:80 volumes: - /var/run/docker.sock:/var/run/docker.sock # 执行如下命令即可实现负载均衡,数字3可修改为其它想要启动的容器数 docker-compose up --scale wordpress=3 -d |
容器编排Docker Swarm
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
# 在 Swarm Manager上运行 docker swarm init --advertise-addr=your.ip.add # 运行结束时会提示,在 Swarm Work 上运行该命令即可 docker swarm join --token <Swarm Manager Token> your.manager.ip.addr:2377 # 执行完成后可在 Swarm Manager 上查看节点 docker node ls # 在 Swarm Manager 上创建Service docker service create --name demo busybox sh -c "while true; do sleep 3600; done" # 查看 service docker service ls # 查看指定 service所在节点等信息 docker service ps demo # 水平扩展 docker service scale demo=3 # 删除 docker service rm demo |
基于上述Docker Swarm 集群部署 WordPress
1 2 3 4 5 6 |
# 创建 Overlay 网络(manager) docker network create -d overlay demo # 安装 MySQL docker service create --name mysql --env MYSQL_ROOT_PASSWORD=root --env MYSQL_DATABASE=wordpress --network demo --mount type=volume,source=mysal-data,destination=/var/lib/mysql mysql:5.7 # 安装 Wordpress docker service create --name wordpress -p 80:80 --env WORDPRESS_DB_PASSWORD=root --env WORDPRESS_DB_HOST=mysql --network demo wordpress |
此时便完成了 WordPress 的部署,我们会发现通过所有节点的 IP 均可访问该站点
集群服务器间通信 Routing Mesh测试
1 2 |
docker service create --name whoami -p 8000:8000 --network demo -d jwilder/whoami docker service create --name client -d --network demo busybox sh -c "while true; do sleep 3600; done" |
Routing Mesh两种体现
- Internal-容器之间的访问通过 Overlay 网络(通过 VIP 虚拟 IP)
- Ingress-如果服务有绑定接口,则此服务可以通过任意 Swarm 节点的相应接口访问
- 外部访问的负载均衡
- 服务端口被暴露到各个 Swarm 节点
- 内部通过 IPVS 进行负载均衡
Ingress 查看相关命令
1 2 3 4 5 6 |
sudo yum install -y ipvsadm sudo nsenter --net=/var/run/docker/netns/ingress_sbox # 进入 ingress_box 后查看虚拟 IP 指向 sudo iptables -nL -t mangle # 查看具体 IP ipvsadm -l |
Docker Stack部署 WordPress
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
docker stack deploy web --compose-file=docker-compose.yml # docker-compose.yml 修改网络为 overlay,MySQL 仅允许在 Manager 节点上 version: '3' services: wordpress: image: wordpress ports: - 80:80 environment: WORDPRESS_DB_HOST: mysql WORDPRESS_DB_PASSWORD: root networks: - my-network depends_on: - mysql deploy: mode: replicated replicas: 3 restart_policy: condition: on-failure delay: 5s max_attempts: 3 update_config: parallelism: 1 delay: 10s mysql: image: mysql:5.7 environment: MYSQL_ROOT_PASSWORD: root MYSQL_DATABASE: wordpress volumes: - mysql-data:/var/lib/mysql networks: - my-network deploy: mode: global placement: constraints: - node.role == manager volumes: mysql-data: networks: my-network: driver: overlay |
Docker Secret Management
上面的 docker-compose.yml 中包含数据库密码等敏感信息,类似的信息有:用户名密码、SSH Key、TLS认证和任何不想让其它人看到的数据。对于这类似信息我们需要通过加密的方式进行存储同时 Service 还可以获取
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
# 从文件创建 docker secret create my-pw password.txt # 命令行创建 echo "xxxxxx" | docker secret create my-pw - # 查看和删除 docker secret ls docker secret rm # 以 MySQL为例(--secret 指定前面创建的密钥,默认会在/run/secrets 下创建一个同名的文件并包含对应信息) docker service create --name db --secret my-pw -e MYSQL_ROOT_PASSWORD_FILE=/run/secrets/my-pw mysql # 同样以 Wordpress 为例,可以前面 Docker Stack 部署的docker-compose.yml文件进行如下修改 environment: WORDPRESS_DB_HOST: mysql WORDPRESS_DB_PASSWORD: root # 修改为 secrets: - my-pw environment: WORDPRESS_DB_HOST: mysql WORDPRESS_DB_PASSWORD_FILE: /run/secrets/my-pw |
Service 更新
1 2 3 4 5 6 |
# 假设有一个名为 web 的 service,首先进行横向扩展,确保更新时服务不中断 docker service scale web=2 # image 更新 docker service update --image xxx/xxx web # 端口更新 docker service update --publish-rm 8080:5000 --publish-add 8888:5000 web |
DevOps初体验—Docker Cloud和Docker企业版
CaaS(Container-as-a-Service)
Docker Cloud:提供容器的管理、编排、部署的托管服务
主要模块:关联云服务商、添加节点行为、创建服务、创建 Stack、Image 管理
企业版安装
Docker Store 中申请30天试用,
Docker EE安装方法参见文档(以 CentOS为例)
安装 UCP
注:测试安装时发现版本仅为17.06.2,当前新版的 UCP 需要18以上的企业版,因而选择低版本兼容的 UCP
1 2 3 4 5 |
docker container run --rm -it --name ucp \ -v /var/run/docker.sock:/var/run/docker.sock \ docker/ucp:2.2.4 install \ --host-address <node-ip-address> \ --interactive |
点击 Nodes>Add Node,在 worker 节点服务器上执行生成的docker swarm join 代码段
DTR安装
1 2 3 |
docker tag image-name:latest dtr.ip.addr/admin/demo docker login dtr.ip.addr docker push dtr.ip.addr/admin/demo |
此外在 System>Security中 Enable Sanning 用于开启对镜像的安全扫描功能,此时即可对仓库中的镜像进行安全扫描:
容器编排Kubernetes
Minikube
使用 minikube 进行安装单节点 K8S集群
1 2 3 4 5 6 7 8 9 |
brew cask install minikube # 启动 minikube start # 查看设置及上下文 kubectl config view kubectl config get-contexts kubectl cluster-info # 进入节点 minikube ssh |
K8S最小调度单位 Pod,一个 Pod 共享一个 Namespace
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
# 创建 Pod kubectl create -f xxx.yml # 删除 Pod kubectl delete -f xxx.yml # 查看 Pod kubectl get pods # 显示更多信息 kubectl get pods -o wide # 进入 Pod kubectl exec -it xxx sh # 查看 Pod 详细信息 kubectl describe pods xxx # 端口转发,以 Nginx pod 为例 kubectl port-forward nginx 8080:80 # ReplicationController # apiVersion: v1 # kind: ReplicationController # 查看 kubectl get rc # 扩展 kubectl scale rc nginx --replicas=2 # ReplicaSet # apiVersion: apps/v1 # kind: ReplicaSet # 查看 kubectl get rs # 扩展 kubectl scale rs nginx --replicas=2 # Deployment # apiVersion: apps/v1 # kind: Deployment kubectl get deployment kubectl get deployment -o wide kubectl get pods # 设置镜像(更新操作) kubectl set image deployment nginx-deployment nginx=nginx:1.13 # 查看历史记录 kubectl rollout history deployment nginx-deployment # 回复到上一个版本 kubectl rollout undo deployment nginx-deployment # 创建 service 供外部访问及查看端口 kubectl expose deployment nginx-deployment --type=NodePort kubectl get svc |
Tectonic
使用Tectonic 搭建多节点集群
kubectl expose命令会给 Pod 创建 Service 供外部访问,Service 主要有三种类型:ClusterIP, NodePort, 外部的LoadBalancer(kubectl expose 通过–type 指定); 也可以使用 DNS,但需要 DNS 的 add-on
容器的的运维和监控
1 2 |
docker top docker state |
Weave Scope
1 2 3 |
sudo curl -L git.io/scope -o /usr/local/bin/scope sudo chmod a+x /usr/local/bin/scope scope launch |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
minikube addons list minikube addons enable heapster # 重启 minikube stop minikube start --extra-config=controller-manager.HorizontalPodAutoscalerUseRESTClients=false # 自动横向扩展 # 运行镜像并暴露端口 kubectl run php-apache --image=k8s.gcr.io/hpa-example --requests=cpu=200m --expose --port=80 # 创建 HPA( Horizontal Pod Autoscaler),指定最小和最大数量,超过50%后就会扩展 kubectl autoscale deployment php-apache --cpu-percent=50 --min=1 --max=10 # 查看 kubectl get deployment php-apache kubectl get horizontalpodautoscaler kubectl get svc # 删除部署 kubectl delete deployment php-apache kubectl delete svc php-apache kubectl delete hpa php-apache |
Horizontal Pod Autoscaler Walkthrough
日志采集展示
ELK Stack(ElasticSearch+Logstash+Kibana)
- Fluentd(log 转发)
- ElasticSearch(log Index)
- Kibana(log 可视化)
- LogTrail(log UI 查看)
本地搭建集群:kubeadm
相应 yml 文件(替换成对应版本):fluentd-elastisearch
集群监控方案(Pull 数据):https://prometheus.io/
Docker+DevOps实战—过程和工具
基本流程参见容器的的运维和监控部分的图片
安装 GitLab,参见Jenkins+Ansible+GitLab自动化部署三剑客
GitLab CI 服务器搭建
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
# 安装 Docker curl -sSL https://get.docker.com/ | sh # 安装 gitlab ci runner curl -L https://packages.gitlab.com/install/repositories/runner/gitlab-ci-multi-runner/script.rpm.sh | sudo bash sudo yum install gitlab-ci-multi-runner -y # 查看是否运行正常 sudo gitlab-ci-multi-runner status # 设置 Docker 权限 sudo usermod -aG docker gitlab-runner sudo service docker restart sudo gitlab-ci-multi-runner restart # 注册 sudo gitlab-ci-multi-runner register # 输入 GitLab 地址和 token(项目页>Settings>CI/CD>Runners 下的Set up a specific Runner manually 中) # 设置完成后在同一版块的Runners activated for this project中会发现新增的Runner,激活并在项目下添加.gitlab-ci.yml文件测试,结果在CI/CD>Pipelines 中查看 |
Settings>Repository>Protected Branches可设置禁止 master 分支的 push 行为
Repository>Branches 创建新分支(如 dev)
Settings>General>Merge Request 可进行相关限制
1 2 3 4 |
git fetch git checkout dev git push origin dev # Merge Requests 中进行提交 |
监控方案:cAdvisor + InfluxDB + Grafana
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
# InfluxDB docker run -d -p 8083:8083 -p 8086:8086 --name influxdb tutum/influxdb # 创建数据库、用户名及密码(以下假定均为cadvisor) # cAdvisor docker run -d \--volume=/:/rootfs:ro \ --volume=/var/run:/var/run:rw \ --volume=/sys:/sys:ro \ --volume=/var/lib/docker/:/var/lib/docker:ro \ --link influxdb:influxdb \ -p 8081:8080 \ --name=cadvisor \ google/cadvisor:latest \ -storae_driver=influxdb \ -storage_driver_db=cadvisor \ -storage_driver_host=influxdb:8086 # Grafana docker run -d \ -p 3000:3000 \ -e INFLUXDB_HOST=influxdb\ -e INFLUXDB_PORT=8086 \ -e INFLUXDB_NAME=cadvisor \ -e INFLUXDB_USER=cadvisor \ -e INFLUXDB_PASS=cadvisor \ --link influxdb:influxsrv \ --name grafana \grafana/grafana |
监控方案:Prometheus + Grafana
常见问题
1、Error restarting cluster: restarting kube-proxy: waiting for kube-proxy to be up for configmap update: timed out waiting for the condition
1 2 3 |
# 删除重新执行 minikube delete minikube start |
2、容器状态为 DEAD 无法删除
1 2 3 |
docker ps -a --no-trunc # 查看 DOCKER ID sudo umount /var/lib/docker/aufs/mnt/xxx docker rm xxx |
3、镜像直推另一台主机
1 2 3 4 5 |
docker save <image> | bzip2 | pv | \ ssh user@host 'bunzip2 | docker load' # 方法2 docker save -o <path for generated tar file> <image name> docker load -i <path to image tar file> # 拷贝到目标主机加载 |
4、镜像日志未彻底删除
1 2 3 4 |
lsof|grep delete # 查看到文件未彻底删除 /etc/init.d/docker restart # 重启服务即会彻底删除 # 若想恢复或查看记下第二列的 pid 和第4列的数字,如第二列为1775,第4列为4r,则通过如下文件可进行查看: cat /proc/1775/fd/4 |
5、Docker中Nginx的 Gzip 开启问题
使用 Docker的Nginx 镜像会发现在开启了 gzip后实际并未生效,而对容器查看发现确实包含了--with-http_gzip_static_module
,这就有些让人不解了,后来发现需设置gzip_http_version 1.0;
才会生效
一些测试命令
1 2 |
curl -I -H "Accept-Encoding: gzip, deflate" "http://xxx.xxx/xx" curl x --silent -H "Accept-Encoding: gzip,deflate" --write-out "%{size_download}\n" --output /dev/null |