赞
踩
一、背景介绍
由于国内网络原因,gcr.io 仓库里的镜像是无法直接拉取到的,这给开发工作造成了极大的不便
本文介绍一种方法能够实现自动化地定期地将 gcr.io 仓库中的镜像同步到个人 DockerHub 账户
实现该方案需要满足以下条件:
a) 已注册 GitHub 账号 https://github.com/
b) 已注册 DockerHub 账号 https://hub.docker.com
c) 已注册 Google Cloud 账号 (需要 Fan Qiang)https://cloud.google.com/
d) 已注册 Travis CI 账号 (可以使用 GitHub 账号登录)https://www.travis-ci.org/
二、实现步骤
2.1 创建一个 Google Cloud 服务账户,以读取 gcr.io 仓库中的镜像列表
首先,登录 Google Cloud 控制台,点击菜单 “IAM和管理” -> “服务账号”
(该功能需要在某个项目下操作,如果还没有项目,可以点 [创建] 新建一个项目,我的项目为 gcr-scan)
填入 “账号名称” 和 “账号ID”,点击 “创建”,之后授予权限,角色选择 “容器分析备注查看者”,点击 “继续”
点击 “创建密钥”
选择 json 格式,点击 “创建”,会提示下载该 json 文件,把它命名为 gcloud.config.json ,保存起来备用
该文件是实现读取 gcr.io 仓库中镜像列表的关键
2.2 获取 DockerHub 登录密钥文件
找一台安装有 Docker 的机器,执行 `docker login` 命令
输入你的 DockerHub 用户名和密码,提示 Login Succeeded 即登录成功
密钥文件会自动生成在 ~/.docker/config.json,将该 config.json 文件保存备用
2.3 创建 SSH 密钥,并在 GitHub 上进行授权
首先在本地生成一个 SSH 密钥
# ssh-keygen -t RSA -P ""
生成的私钥文件位于 ~/.ssh/id_rsa,将该文件保存备用
另外需要将 ~/.ssh/id_rsa.pub 公钥内容配置到 GitHub
登录 GitHub,进入 “Settings” 菜单,点击 “New SSH key”
填入你的 SSH 公钥信息,点击 “Add SSH key”
2.4 创建 GitHub 项目,配置 Travis CI 策略
创建的 GitHub 项目名称任意,可以参考我的 https://github.com/SataQiu/gcr_sync
然后克隆项目到本地:git clone https://github.com/<你的GitHub账户名>/<你起的项目名称>
首先,创建一个 .travis.yml 文件,放置到项目根目录,该文件告诉 travis ci 如何构建该项目
然后,将 2.1、 2.2、 2.3 步分别生成的 gcloud.config.json、config.json、id_rsa 放置到项目根目录
将三个文件打包到一起,执行 :
# tar czvf config.tar.gz gcloud.config.json config.json id_rsa
紧接着,我们需要对 config.tar.gz 进行加密,该过程需要 travis 命令行工具,为了省去安装 travis 的麻烦
我们通过容器来执行文件加密,假设你的项目路径位于 /home/shida/src/gcr_sync
# docker run -it -v /home/shida/src/gcr_sync:/root shidaqiu/travis-cli
在容器里,登录 travis
# travis login
输入 Travis CI 账号密码(其实就是 GitHub 账号密码)
登录成功后,执行加密命令
# travis encrypt-file conf.tar.gz --add
如果这里加密不成功,就需要登录 Travis CI 先关联上 GitHub 项目,点击 “Sync Accont”
该加密命令会生成一个 conf.tar.gz.enc 加密文件,同时修改 .travis.yml 文件
加密完成,退出容器即可
之后,在项目根目录创建一个文件夹,命名为 .travis
将 conf.tar.gz.enc 文件移动到该文件夹,并从项目删除加密前的原文件
# rm -f conf.tar.gz gcloud.config.json config.json id_rsa
修改 .travis.yml
注意 openssl aes-256-cbc 这句不要照抄我的,要保持加密完文件后该文件里面默认的值
当执行完加密文件操作,openssl aes-256-cbc 这句会自动生成到 .travis.yml 里
sudo: required
language: python
python:
- '2.7'
addons:
apt:
packages:
- docker-ce
branches:
only:
- master
install:
- git remote -v
script:
- "bash ./sync.sh 10"
before_install:
- export start_time=$(date +%s)
- mkdir -p ~/.docker
- mkdir -p ~/.ssh
- openssl aes-256-cbc -K $encrypted_0a93db31c783_key -iv $encrypted_0a93db31c783_iv
-in .travis/conf.tar.gz.enc -out ~/conf.tar.gz -d
- tar zxf ~/conf.tar.gz -C ~
- mv ~/config.json ~/.docker/
- mv ~/id_rsa ~/.ssh/
- chmod 600 ~/.ssh/id_rsa
- chmod 600 ~/.docker/config.json
- eval $(ssh-agent)
- ssh-add ~/.ssh/id_rsa
- df -h
然后,在根目录创建 sync.sh 脚本
注意修改 MY_REPO 为你的 DockerHub 账户名,以及对应修改 git_init() 函数中的账户信息为你自己的
#!/bin/bash
max_process=$1
MY_REPO=shidaqiu
interval=.
max_per=70
google_list=repo/google
#--------------------------Multi_process_init() {
trap 'exec 5>&-;exec 5<&-;exit 0' 2
pipe=`mktemp -u tmp.XXXX`
mkfifo $pipe
exec 5<>$pipe
rm -f $pipe
seq $1 >&5
}git_init(){
git config --global user.name "SataQiu"
git config --global user.email xxx@qq.com
git remote rm origin
git remote add origin git@github.com:SataQiu/gcr_sync.git
git pull
if git branch -a |grep 'origin/develop' &> /dev/null ;then
git checkout develop
git pull origin develop
git branch --set-upstream-to=origin/develop develop
else
git checkout -b develop
git pull origin develop
fi
}git_commit(){
local COMMIT_FILES_COUNT=$(git status -s|wc -l)
local TODAY=$(date +%F)
if [[ $COMMIT_FILES_COUNT -ne 0 && $(( (`date +%s` - start_time)/60 )) -gt 45 ]];then
git add -A
git commit -m "Synchronizing completion at $TODAY"
git push -u origin develop
fi
}add_yum_repo() {
cat > /etc/yum.repos.d/google-cloud-sdk.repo <<EOF
[google-cloud-sdk]
name=Google Cloud SDK
baseurl=https://packages.cloud.google.com/yum/repos/cloud-sdk-el7-x86_64
enabled=1
gpgcheck=1
repo_gpgcheck=1
gpgkey=https://packages.cloud.google.com/yum/doc/yum-key.gpg
https://packages.cloud.google.com/yum/doc/rpm-package-key.gpg
EOF
}add_apt_source(){
export CLOUD_SDK_REPO="cloud-sdk-$(lsb_release -c -s)"
echo "deb http://packages.cloud.google.com/apt $CLOUD_SDK_REPO main" | sudo tee -a /etc/apt/sources.list.d/google-cloud-sdk.list
curl https://packages.cloud.google.com/apt/doc/apt-key.gpg | sudo apt-key add -
}install_sdk() {
local OS_VERSION=$(grep -Po '(?<=^ID=")\w+' /etc/os-release)
local OS_VERSION=${OS_VERSION:-ubuntu}
if [[ $OS_VERSION =~ "centos" ]];then
if ! [ -f /etc/yum.repos.d/google-cloud-sdk.repo ];then
add_yum_repo
yum -y install google-cloud-sdk
else
echo "gcloud is installed"
fi
elif [[ $OS_VERSION =~ "ubuntu" ]];then
if ! [ -f /etc/apt/sources.list.d/google-cloud-sdk.list ];then
add_apt_source
sudo apt-get -y update && sudo apt-get -y install google-cloud-sdk
else
echo "gcloud is installed"
fi
fi
}auth_sdk(){
local AUTH_COUNT=$(gcloud auth list --format="get(account)"|wc -l)
[ "$AUTH_COUNT" -eq 0 ] && gcloud auth activate-service-account --key-file=$HOME/gcloud.config.json ||
echo "gcloud service account is exsits"
}
# GCR_IMAGE_NAME tag REPO_IMAGE_NAME
image_tag(){
docker pull $1:$2
docker tag $1:$2 $3:$2
docker rmi $1:$2
}img_clean(){
local domain=$1 namespace=$2 image_name=$3
local Prefix=$domain$interval$namespace$interval
shift 3
while read img tag null;do
docker push $img:$tag;docker rmi $img:$tag;
[ "$tag" != latest ] && echo $domain/$namespace/$image_name:$tag > $domain/$namespace/$image_name/$tag ||
$@ $domain/$namespace/$image_name > $domain/$namespace/$image_name/$tag
git_commit
done < <(docker images --format {{.Repository}}' '{{.Tag}}' '{{.Size}} | awk -vcut=$MY_REPO/$Prefix '$0~cut{print $0 | "sort -hk3" }')
git_commit
}# google::name(){
# gcloud container images list --repository=$@ --format="value(NAME)"
# }
# google::tag(){
# gcloud container images list-tags $@ --format="get(TAGS)" --filter='tags:*' | sed 's#;#\n#g'
# }
# google::latest::digest(){
# gcloud container images list-tags --format='get(DIGEST)' $@ --filter="tags=latest"
# }google::name(){
curl -XPOST -ks 'https://console.cloud.google.com/m/gcr/entities/list' \
-H 'User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.7 Safari/537.36' \
-H 'Content-Type: application/json;charset=UTF-8' \
-H 'Accept: application/json, text/plain, */*' \
--data-binary ['"'"${@#*/}"'"'] |
awk -vio=$@ -F'"' '/"/{if(NR==3){if(!a[$4]++)print io"/"$4}else{if(!a[$2]++)print io"/"$2}}'
}
google::tag(){
read null ns name< <(tr '/' ' '<<<$@)
curl -ks -XGET https://gcr.io/v2/${ns}/${name}/tags/list | jq -r .tags[]
}
google::latest_digest(){
read null ns name< <(tr '/' ' '<<<$@)
curl -ks -XGET https://gcr.io/v2/${ns}/${name}/tags/list | jq -r '.manifest | with_entries(select(.value.tag[] == "latest"))|keys[]'
}#quay::name(){
# NS=${1#*/}
# curl -sL 'https://quay.io/api/v1/repository?public=true&namespace='${NS} | jq -r '"quay.io/'${NS}'/"'" + .repositories[].name"
#}
#quay::tag(){
# curl -sL "https://quay.io/api/v1/repository/${@#*/}?tag=info" | jq -r .tags[].name
#}
#quay::latest_digest(){
# # curl -sL "https://quay.io/api/v1/repository/prometheus/alertmanager/tag" | jq -r '.tags[]|select(.name == "latest" and (.|length) == 5 ).manifest_digest'
# curl -sL "https://quay.io/api/v1/repository/${@#*/}?tag=info" | jq -r '.tags[]|select(.name == "latest" and (has("end_ts")|not) ).manifest_digest'
#}
image_pull(){
REPOSITORY=$1
echo 'Sync the '$REPOSITORY
shift
domain=${REPOSITORY%%/*}
namespace=${REPOSITORY##*/}
Prefix=$domain$interval$namespace$interval
# REPOSITORY is the name of the dir,convert the '/' to '.',and cut the last '.'
[ ! -d "$domain/$namespace" ] && mkdir -p $domain/$namespace
while read SYNC_IMAGE_NAME;do
image_name=${SYNC_IMAGE_NAME##*/}
MY_REPO_IMAGE_NAME=${Prefix}${image_name}
[ ! -d "$domain/$namespace/$image_name" ] && mkdir -p "$domain/$namespace/$image_name"
[ -f "$domain/$namespace/$image_name"/latest ] && mv $domain/$namespace/$image_name/latest{,.old}
while read tag;do
#处理latest标签
[[ "$tag" == latest && -f "$domain/$namespace/$image_name"/latest.old ]] && {
$@::latest_digest $SYNC_IMAGE_NAME > $domain/$namespace/$image_name/latest
diff $domain/$namespace/$image_name/latest{,.old} &>/dev/null &&
{ rm -f $domain/$namespace/$image_name/latest.old;continue; } ||
rm $domain/$namespace/$image_name/latest{,.old}
}
[ -f "$domain/$namespace/$image_name/$tag" ] && { trvis_live;continue; }
[[ $(df -h| awk '$NF=="/"{print +$5}') -ge "$max_per" || -n $(sync_commit_check) ]] && { wait;img_clean $domain $namespace $image_name $@::latest_digest; }
read -u5
{
[ -n "$tag" ] && image_tag $SYNC_IMAGE_NAME $tag $MY_REPO/$MY_REPO_IMAGE_NAME
echo >&5
}&
done < <($@::tag $SYNC_IMAGE_NAME)
wait
img_clean $domain $namespace $image_name $@::latest_digest
done < <($@::name $REPOSITORY)
}sync_commit_check(){
[[ $(( (`date +%s` - start_time)/60 )) -gt 40 || -n "$(docker images | awk '$NF~"GB"')" ]] &&
echo ture || false
}# img_name tag
hub_tag_exist(){
curl -s https://hub.docker.com/v2/repositories/${MY_REPO}/$1/tags/$2/ | jq -r .name
}
trvis_live(){
[ $(( (`date +%s` - live_start_time)/60 )) -ge 8 ] && { live_start_time=$(date +%s);echo 'for live in the travis!'; }
}sync_domain_repo(){
path=${1%/}
local name tag
while read name tag;do
img_name=$( sed 's#/#'"$interval"'#g'<<<$name )
trvis_live
read -u5
{
[ "$( hub_tag_exist $img_name $tag )" == null ] && rm -f $name/$tag
echo >&5
}&
done < <( find $path/ -type f | sed 's#/# #3' )
wait
git_commit
}
main(){
[ -z "$start_time" ] && start_time=$(date +%s)
git_init
# install_sdk
# auth_sdk
Multi_process_init $(( max_process * 4 ))
live_start_time=$(date +%s)
read sync_time < sync_check
[ $(( (`date +%s` - sync_time)/3600 )) -ge 6 ] && {
[ ! -f sync_list_ns ] && ls gcr.io > sync_list_ns
allns=(`xargs -n1 < sync_list_ns`)for ns in ${allns[@]};do
[ ! -f sync_list_name ] && ls gcr.io/$ns > sync_list_name
allname=(`xargs -n1 < sync_list_name`)
for name in ${allname[@]};do
sync_domain_repo gcr.io/$ns/$name
perl -i -lne 'print if $_ ne "'$name'"' sync_list_name
done
rm -f sync_list_name
perl -i -lne 'print if $_ ne "'$ns'"' sync_list_ns
done
rm -f sync_list_ns
echo the sync has done!
date +%s > sync_check
git_commit
}
exec 5>&-;exec 5<&-
Multi_process_init $max_processGOOLE_NAMESPACE=(`xargs -n1 < $google_list`)
for repo in ${GOOLE_NAMESPACE[@]};do
image_pull gcr.io/$repo google
sed -i '/'"$repo"'/d' $google_list;echo "$repo" >> $google_list
doneexec 5>&-;exec 5<&-
COMMIT_FILES_COUNT=$(git status -s|wc -l)
TODAY=$(date +%F)
if [ $COMMIT_FILES_COUNT -ne 0 ];then
git add -A
git commit -m "Synchronizing completion at $TODAY"
git push -u origin develop
fi
}main
接着,在项目根目录建一个文件夹 repo,在文件夹内建一个文件,起名为 google,文件内容为镜像 NAMESPACE
我这里只同步 google_containers 下的镜像,因此文件内容为
google_containers
如果你还需要同步其他 NAMESPACE,每行写一个 NAMESPACE 即可
然后就可以将该项目推送到 GitHub 了
- # git add .
- # git commit -m "init"
- # git push origin master
2.5 启动自动构建
使用 GitHub 账号登录 Travis CI
选择上一步建立的 GitHub 项目,开启自动构建,同时点击 Settings
设置定时构建
选择 Current Tab 页,也可以手动触发构建过程
免费的 Travis CI 最长构建时间为 50 分钟,这通常是同步不完的,没关系,反复执行构建过程就行了
已经同步过的镜像不会重复同步
去你的 DockerHub 账户下查看下,应该已经有镜像同步过来了
因为 DockerHub 不允许名称中包含 " / " ,因此这里使用了 " . " 作为连接符
使用的时候需要重命名下,例如:
- # docker pull shidaqiu/gcr.io.google_containers.cloud-controller-manager:v1.11.1
- # docker tag shidaqiu/gcr.io.google_containers.cloud-controller-manager:v1.11.1 gcr.io/google_containers/cloud-controller-manager:v1.11.1
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。