容器的生命期的问题
-i 交互
-t 终端
-d 守护进程(后台运行)
-e 指定变量
--rm 临时容器 --restart=always 不能同时使用
--name 指定容器名
# 进入容器
docker attach <容器ID>
docker run -dit --name=c1 --restart=always hub.c.163.com/library/centos
# 查看容器信息
docker inspect c1
docker top <容器名>
# 读取容器里面的输出
docker logs <容器名>
# 在容器mysql镜像,指定了一个变量
# 额外的打开一个bash进程
# 冒号左边是物理机,冒号右边是容器
- docker exec <容器名> 命令
- docker start <容器名>
- docker stop <容器名>
- docker restrart <容器名>
- docker top <容器名>
- docker logs -f <容器名>
- docker inspect <容器名>
- docker cp <拷贝的文件> <容器名>:<目录> docker cp <容器名>:<目录> <物理机存放地址> # 容器之间不能这样拷贝
weavescope 工具安装测试
curl -L git.io/scope -o /usr/local/bin/scope
赋予可执行权限:
chmod a+x /usr/local/bin/scope
启动scope:
scope launch
数据卷的使用
-v /dir # 默认指定一个目录,是容器里面的目录。物理机是随机生成一个目录
-v /dir2:dir # 冒号左边是物理机里面的目录,冒号右边的是容器目录
-v /dir2:/dir:rw # 默认
-v /dir2:/dir:ro # 需要手动指定只读
你得去理解容器里的存储信息(比如:mysql本身存放在哪里的)
# 需要自己再熟悉下 apache tomcat redis
docker网络管理
# 查看容器ip地址
# 查看本地网桥
docker network list
# 查看网络属性
docker network inspect bridge
# 会生成docker0
# 自己创建一个网桥
docker network create -d bridge --subnet=x.x.x.x/24 <网桥名>
PS: 如何自己通过命令查询 创建网络命令
容器网络里面有
bridge 网桥 相当于nat
host 直接变成主机网络
none 不设置网络(单独配合一些工具可以变成桥接模式,设置独立IP地址)
# 加入到自己的创建的网桥
# 查看应用所需要的变量,例如wordpress
PS:可以使用hub或者163里面的 how to use 来看下所需的变量意思
网易:https://c.163yun.com/hub#/home
# 查找docker 所有相关帮助命令
man -k docker
# 新建一个数据库,使用了两个变量(一个是设置root密码,一个是创建数据库)
# 创建wordpress (正常方式)
# 查看更详细的信息
docker history --no-trunc
# 再次新建后使用 --link 别名 来省略一些变量参数,并且防止容器内部IP变化导致的配置文件修改 【这部分需要进行试验一下】
自定义镜像
构建镜像:并非是从0到1的过程,在一个已经存在的镜像的基础上,进行构建出新的镜像
基镜像,基础镜像
最底层的---是一个干净的操作系统,一般是由厂商来提供
Dockefile
#############
RUN
ADD
ENV
VOLUME
CMD
#############
# 通过dockerfile 生成容器
1. 生成临时容器,执行dockerfile 内容,产生容器,最后删除临时容器
Dockefile 文件举例:
tar tf <tar> 查看tar包里面内容
docker build -t centos:v0 .
. 当前目录去找(其他目录就用-f [dockerfile文件名],不跟文件名,默认寻找Dockerfile)
ADD 和 COPY 区别 (需实验)
自己搭建一个nginx 镜像
# 可以自己修改nginx配置文件
# 守护进程模式开启的方法, docker history --no-trunc | tail -2
EXPOSE 仅仅是个标识 <--- 告诉别人你默认用的端口是什么
# 如何判断 CMD 后面需要填写的守护进程参数(需要根据经验,或者找一个镜像通过docker histroy查看)
Nginx:
CMD ["nginx", "-g", "daemon off;"]
SSH:
CMD["sshd", "-D"]
Mysql:
CMD ["mysqld"]
Centos:
CMD ["/bin/bash"]
# ENV 和 用户添加
ENV
USER
# Dockerfile RUN 尽量放在一行来写,会减少镜像层数,从而减小镜像的大小
# 在dockerfile 里面指定用户执行进程,并设置变量 aa
# 如果创建的进程是普通用户,想用root进入 docker exec -it -uroot .....的方式
# VOLUME 定义卷
VOLUME ["dir1", "dir2"]
# ssh 的dockerfile 是如何一步步做出这个文件的
私有仓库
# 搭建自己的私有仓库
1. registry
2. harbor
使用registry 搭建私有仓库
registry 端口5000
/var/lib/registry
1. 仅允许https 方式访问
http+ssl -- tls(传输层的加密)
两种只能选一种,不要都使用
# 方法一:
修改 不适用https 的方式,都需要修改
/etc/docker/daemon.json
# 方法二:
# 从客户端推送镜像到私有仓库
# 修改tag
# 再进行推送(推送的时候一定要注意镜像tag是不是复核规范)
#使用curl命令查询的镜像数量
# 查询镜像的版本
使用脚本来实现查询镜像和版本
# 老师的脚本,仅供参考
1 #!/bin/bash 2 file=$(mktemp) 3 curl -s $1:5000/v2/_catalog | jq | egrep -v '{|}|[|]' | awk -F" '{print $2}' > $file 4 while read aa ; do 5 tag=($(curl -s $1:5000/v2/$aa/tags/list | jq | egrep -v '{|}|[|]|name' | awk -F" '{print $2}')) 6 for i in ${tag[*]} ; do 7 echo $1:5000/${aa}:$i 8 done 9 done < $file 10 rm -rf $file
# 用脚本查询结果
# 删除私有仓库下面多余或无用的镜像
用老师的PY脚本
#!/usr/bin/env python """ Usage: Shut down your registry service to avoid race conditions and possible data loss and then run the command with an image repo like this: delete_docker_registry_image.py --image awesomeimage --dry-run """ import argparse import json import logging import os import sys import shutil import glob logger = logging.getLogger(__name__) def del_empty_dirs(s_dir, top_level): """recursively delete empty directories""" b_empty = True for s_target in os.listdir(s_dir): s_path = os.path.join(s_dir, s_target) if os.path.isdir(s_path): if not del_empty_dirs(s_path, False): b_empty = False else: b_empty = False if b_empty: logger.debug("Deleting empty directory '%s'", s_dir) if not top_level: os.rmdir(s_dir) return b_empty def get_layers_from_blob(path): """parse json blob and get set of layer digests""" try: with open(path, "r") as blob: data_raw = blob.read() data = json.loads(data_raw) if data["schemaVersion"] == 1: result = set([entry["blobSum"].split(":")[1] for entry in data["fsLayers"]]) else: result = set([entry["digest"].split(":")[1] for entry in data["layers"]]) if "config" in data: result.add(data["config"]["digest"].split(":")[1]) return result except Exception as error: logger.critical("Failed to read layers from blob:%s", error) return set() def get_digest_from_blob(path): """parse file and get digest""" try: with open(path, "r") as blob: return blob.read().split(":")[1] except Exception as error: logger.critical("Failed to read digest from blob:%s", error) return "" def get_links(path, _filter=None): """recursively walk `path` and parse every link inside""" result = [] for root, _, files in os.walk(path): for each in files: if each == "link": filepath = os.path.join(root, each) if not _filter or _filter in filepath: result.append(get_digest_from_blob(filepath)) return result class RegistryCleanerError(Exception): pass class RegistryCleaner(object): """Clean registry""" def __init__(self, registry_data_dir, dry_run=False): self.registry_data_dir = registry_data_dir if not os.path.isdir(self.registry_data_dir): raise RegistryCleanerError("No repositories directory found inside " "REGISTRY_DATA_DIR '{0}'.". format(self.registry_data_dir)) self.dry_run = dry_run def _delete_layer(self, repo, digest): """remove blob directory from filesystem""" path = os.path.join(self.registry_data_dir, "repositories", repo, "_layers/sha256", digest) self._delete_dir(path) def _delete_blob(self, digest): """remove blob directory from filesystem""" path = os.path.join(self.registry_data_dir, "blobs/sha256", digest[0:2], digest) self._delete_dir(path) def _blob_path_for_revision(self, digest): """where we can find the blob that contains the json describing this digest""" return os.path.join(self.registry_data_dir, "blobs/sha256", digest[0:2], digest, "data") def _blob_path_for_revision_is_missing(self, digest): """for each revision, there should be a blob describing it""" return not os.path.isfile(self._blob_path_for_revision(digest)) def _get_layers_from_blob(self, digest): """get layers from blob by digest""" return get_layers_from_blob(self._blob_path_for_revision(digest)) def _delete_dir(self, path): """remove directory from filesystem""" if self.dry_run: logger.info("DRY_RUN: would have deleted %s", path) else: logger.info("Deleting %s", path) try: shutil.rmtree(path) except Exception as error: logger.critical("Failed to delete directory:%s", error) def _delete_from_tag_index_for_revision(self, repo, digest): """delete revision from tag indexes""" paths = glob.glob( os.path.join(self.registry_data_dir, "repositories", repo, "_manifests/tags/*/index/sha256", digest) ) for path in paths: self._delete_dir(path) def _delete_revisions(self, repo, revisions, blobs_to_keep=None): """delete revisions from list of directories""" if blobs_to_keep is None: blobs_to_keep = [] for revision_dir in revisions: digests = get_links(revision_dir) for digest in digests: self._delete_from_tag_index_for_revision(repo, digest) if digest not in blobs_to_keep: self._delete_blob(digest) self._delete_dir(revision_dir) def _get_tags(self, repo): """get all tags for given repository""" path = os.path.join(self.registry_data_dir, "repositories", repo, "_manifests/tags") if not os.path.isdir(path): logger.critical("No repository '%s' found in repositories directory %s", repo, self.registry_data_dir) return None result = [] for each in os.listdir(path): filepath = os.path.join(path, each) if os.path.isdir(filepath): result.append(each) return result def _get_repositories(self): """get all repository repos""" result = [] root = os.path.join(self.registry_data_dir, "repositories") for each in os.listdir(root): filepath = os.path.join(root, each) if os.path.isdir(filepath): inside = os.listdir(filepath) if "_layers" in inside: result.append(each) else: for inner in inside: result.append(os.path.join(each, inner)) return result def _get_all_links(self, except_repo=""): """get links for every repository""" result = [] repositories = self._get_repositories() for repo in [r for r in repositories if r != except_repo]: path = os.path.join(self.registry_data_dir, "repositories", repo) for link in get_links(path): result.append(link) return result def prune(self): """delete all empty directories in registry_data_dir""" del_empty_dirs(self.registry_data_dir, True) def _layer_in_same_repo(self, repo, tag, layer): """check if layer is found in other tags of same repository""" for other_tag in [t for t in self._get_tags(repo) if t != tag]: path = os.path.join(self.registry_data_dir, "repositories", repo, "_manifests/tags", other_tag, "current/link") manifest = get_digest_from_blob(path) try: layers = self._get_layers_from_blob(manifest) if layer in layers: return True except IOError: if self._blob_path_for_revision_is_missing(manifest): logger.warn("Blob for digest %s does not exist. Deleting tag manifest: %s", manifest, other_tag) tag_dir = os.path.join(self.registry_data_dir, "repositories", repo, "_manifests/tags", other_tag) self._delete_dir(tag_dir) else: raise return False def _manifest_in_same_repo(self, repo, tag, manifest): """check if manifest is found in other tags of same repository""" for other_tag in [t for t in self._get_tags(repo) if t != tag]: path = os.path.join(self.registry_data_dir, "repositories", repo, "_manifests/tags", other_tag, "current/link") other_manifest = get_digest_from_blob(path) if other_manifest == manifest: return True return False def delete_entire_repository(self, repo): """delete all blobs for given repository repo""" logger.debug("Deleting entire repository '%s'", repo) repo_dir = os.path.join(self.registry_data_dir, "repositories", repo) if not os.path.isdir(repo_dir): raise RegistryCleanerError("No repository '{0}' found in repositories " "directory {1}/repositories". format(repo, self.registry_data_dir)) links = set(get_links(repo_dir)) all_links_but_current = set(self._get_all_links(except_repo=repo)) for layer in links: if layer in all_links_but_current: logger.debug("Blob found in another repository. Not deleting: %s", layer) else: self._delete_blob(layer) self._delete_dir(repo_dir) def delete_repository_tag(self, repo, tag): """delete all blobs only for given tag of repository""" logger.debug("Deleting repository '%s' with tag '%s'", repo, tag) tag_dir = os.path.join(self.registry_data_dir, "repositories", repo, "_manifests/tags", tag) if not os.path.isdir(tag_dir): raise RegistryCleanerError("No repository '{0}' tag '{1}' found in repositories " "directory {2}/repositories". format(repo, tag, self.registry_data_dir)) manifests_for_tag = set(get_links(tag_dir)) revisions_to_delete = [] blobs_to_keep = [] layers = [] all_links_not_in_current_repo = set(self._get_all_links(except_repo=repo)) for manifest in manifests_for_tag: logger.debug("Looking up filesystem layers for manifest digest %s", manifest) if self._manifest_in_same_repo(repo, tag, manifest): logger.debug("Not deleting since we found another tag using manifest: %s", manifest) continue else: revisions_to_delete.append( os.path.join(self.registry_data_dir, "repositories", repo, "_manifests/revisions/sha256", manifest) ) if manifest in all_links_not_in_current_repo: logger.debug("Not deleting the blob data since we found another repo using manifest: %s", manifest) blobs_to_keep.append(manifest) layers.extend(self._get_layers_from_blob(manifest)) layers_uniq = set(layers) for layer in layers_uniq: if self._layer_in_same_repo(repo, tag, layer): logger.debug("Not deleting since we found another tag using digest: %s", layer) continue self._delete_layer(repo, layer) if layer in all_links_not_in_current_repo: logger.debug("Blob found in another repository. Not deleting: %s", layer) else: self._delete_blob(layer) self._delete_revisions(repo, revisions_to_delete, blobs_to_keep) self._delete_dir(tag_dir) def delete_untagged(self, repo): """delete all untagged data from repo""" logger.debug("Deleting utagged data from repository '%s'", repo) repositories_dir = os.path.join(self.registry_data_dir, "repositories") repo_dir = os.path.join(repositories_dir, repo) if not os.path.isdir(repo_dir): raise RegistryCleanerError("No repository '{0}' found in repositories " "directory {1}/repositories". format(repo, self.registry_data_dir)) tagged_links = set(get_links(repositories_dir, _filter="current")) layers_to_protect = [] for link in tagged_links: layers_to_protect.extend(self._get_layers_from_blob(link)) unique_layers_to_protect = set(layers_to_protect) for layer in unique_layers_to_protect: logger.debug("layer_to_protect: %s", layer) tagged_revisions = set(get_links(repo_dir, _filter="current")) revisions_to_delete = [] layers_to_delete = [] dir_for_revisions = os.path.join(repo_dir, "_manifests/revisions/sha256") for rev in os.listdir(dir_for_revisions): if rev not in tagged_revisions: revisions_to_delete.append(os.path.join(dir_for_revisions, rev)) for layer in self._get_layers_from_blob(rev): if layer not in unique_layers_to_protect: layers_to_delete.append(layer) unique_layers_to_delete = set(layers_to_delete) self._delete_revisions(repo, revisions_to_delete) for layer in unique_layers_to_delete: self._delete_blob(layer) self._delete_layer(repo, layer) def get_tag_count(self, repo): logger.debug("Get tag count of repository '%s'", repo) repo_dir = os.path.join(self.registry_data_dir, "repositories", repo) tags_dir = os.path.join(repo_dir, "_manifests/tags") if os.path.isdir(tags_dir): tags = os.listdir(tags_dir) return len(tags) else: logger.info("Tags directory does not exist: '%s'", tags_dir) return -1 def main(): """cli entrypoint""" parser = argparse.ArgumentParser(description="Cleanup docker registry") parser.add_argument("-i", "--image", dest="image", required=True, help="Docker image to cleanup") parser.add_argument("-v", "--verbose", dest="verbose", action="store_true", help="verbose") parser.add_argument("-n", "--dry-run", dest="dry_run", action="store_true", help="Dry run") parser.add_argument("-f", "--force", dest="force", action="store_true", help="Force delete (deprecated)") parser.add_argument("-p", "--prune", dest="prune", action="store_true", help="Prune") parser.add_argument("-u", "--untagged", dest="untagged", action="store_true", help="Delete all untagged blobs for image") args = parser.parse_args() handler = logging.StreamHandler() handler.setFormatter(logging.Formatter(u'%(levelname)-8s [%(asctime)s] %(message)s')) logger.addHandler(handler) if args.verbose: logger.setLevel(logging.DEBUG) else: logger.setLevel(logging.INFO) # make sure not to log before logging is setup. that'll hose your logging config. if args.force: logger.info( "You supplied the force switch, which is deprecated. It has no effect now, and the script defaults to doing what used to be only happen when force was true") splitted = args.image.split(":") if len(splitted) == 2: image = splitted[0] tag = splitted[1] else: image = args.image tag = None if 'REGISTRY_DATA_DIR' in os.environ: registry_data_dir = os.environ['REGISTRY_DATA_DIR'] else: registry_data_dir = "/opt/registry_data/docker/registry/v2" try: cleaner = RegistryCleaner(registry_data_dir, dry_run=args.dry_run) if args.untagged: cleaner.delete_untagged(image) else: if tag: tag_count = cleaner.get_tag_count(image) if tag_count == 1: cleaner.delete_entire_repository(image) else: cleaner.delete_repository_tag(image, tag) else: cleaner.delete_entire_repository(image) if args.prune: cleaner.prune() except RegistryCleanerError as error: logger.fatal(error) sys.exit(1) if __name__ == "__main__": main()
# 首先,用export定义变量 REGISTRY_DATA_DIR 也就是Registry设置的映射盘路径
# 然后,执行脚本 -i 跟上镜像完整名字
使用harbor 搭建私有仓库
PS: 注意安装的时候上面不能同时存在Registry!!!
# 记得先修改不使用https
# 安装docker 编排工具
yum install docker-compose
# haror 离线包
https://github.com/goharbor/harbor/releases
解压,导入镜像.tar
拷贝模板
cp harbor.yml.tmpl harbor.yml
修改配置文件
注释掉https(生产环境视情况)
默认是80端口(生产环境注意修改)
查看Harbor初始密码(生产环境注意修改)
admin
Harbor12345
运行 准备prepare 脚本
运行 安装 install.sh 脚本
新建项目(tag分类),取名,选择“公开”
【成员】创建用户密码方便用户push镜像过来
# 直接推送,会发现提示需要验证(就是刚刚创建的用户)
注:推送镜像的时候一定要注意命名规则,镜像名字要叫如下方式才可push
<Harbor服务器IP或域名>/<harbor项目名>/<自定义的镜像名>:<TAG>,例如:
118.xxx.xxx.2/cka/c7:loki_v1
118.xxx.xxx.2/cka/busybox:latest
# 登录用户
# 拉取
docker pull 118.xxx.xxx.2/cka/busybox
限制容器资源
目的:防止各个容器资源抢占
Cgroup : linux 系统下的功能
/etc/systemd/system/memload.service.d
# 使用memload 来进行测试
memload <数值>
方法一: 创建容器的时候参数限制
OOM : 内存溢出 需求内存超过限制内存值
# 用来长时间占用cpu 测试
# 查看某个命令运行在哪些CPU上
--cpuset-cpus=0 #指定创建的容器在哪个cpu上运行
# 针对CPU和内存设定值修改方法
docker update
--cpuset-cpus <使用的CPU核心ID> e.g. docker update bbox_t1 --cpuset-cpus 10-15 ## 你要确定你有这么多核心的cpu才能设置这个值
--memory-swap <这个值要大于内存值> ## 如果小于就会如下报错提示 e.g. docker update bbox_t1 --memory-swap 1024m
# 内存限制小于内存的SWAP的限制值,请同步修改内存SWAP值。
Memory limit should be smaller than already set memoryswap limit, update the memoryswap at the same time
-m <内存大小> e.g. docker update bbox_t1 -m 512m
Block IO 是另一种可以限制容器使用的资源。Block IO 指的是磁盘的读写,docker 可通过设置权重、限制 bps 和 iops 的方式控制容器读写磁盘的带宽。
block io 权重
(1)参数说明
- 默认情况下,所有容器能平等地读写磁盘,可以通过设置 --blkio-weight 参数来改变容器 block io 的优先级。
- --blkio–weight 与 --cpu-shares 类似,设置的是相对权重值,默认为 500。
限制 bps 和 iops
- bps 是 byte per second,每秒读写的数据量。
- iops 是 io per second,每秒 IO 的次数。
- --device-read-bps:限制读某个设备的 bps。
- --device-write-bps:限制写某个设备的 bps。
- --device-read-iops:限制读某个设备的 iops。
- --device-write-iops:限制写某个设备的 iops。
ctop 命令研究(仅能检测容器的资源情况)
sudo wget https://github.com/bcicen/ctop/releases/download/v0.7.5/ctop-0.7.5-linux-amd64 -O /usr/local/bin/ctop
sudo chmod +x /usr/local/bin/ctop
# 界面展示
# 基本常用命令 e 进入shell模式,l 进入log模式,o查看容器ip和资源信息,q退出
监控容器
docker stats # 监控容器基本信息
# 原理读取物理机这些目录
cadvisor # google 建立一个监控容器,查看其他容器资源消耗
scope 工具: Weave是由Zett.io公司开发的,它能够创建一个虚拟网络,用于连接部署在多台主机上的Docker容器,这样容器就像被接入了同一个网络交换机,那些使用网络的应用程序不必去配置端口映射和链接等信息。外部设备能够访问Weave网络上的应用程序容器所提供的服务,同时已有的内部系统也能够暴露到应用程序容器上。Weave能够穿透防火墙并运行在部分连接的网络上,另外,Weave的通信支持加密,所以用户可以从一个不受信任的网络连接到主机。
cadvisor: 为了解决docker stats的问题(存储、展示),谷歌开源的cadvisor诞生了,cadvisor不仅可以搜集一台机器上所有运行的容器信息,还提供基础查询界面和http接口,方便其他组件如Prometheus进行数据抓取