https://pkg.go.dev/github.com/docker/docker/client#section-readme
通过golang实现一个简单的镜像下载工具
总体步骤
- 启动一台海外区域的ec2实例
- 安装docker和awscli
- 配置凭证访问国内ecr仓库
- 编写web服务实现镜像转换和自动推送
安装docker和awscli
sudo yum remove awscli -y
sudo yum install less -y
sudo yum install unzip -y
curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
unzip awscliv2.zip
sudo ./aws/install --update
complete -C '/usr/local/bin/aws_completer' aws
sudo yum install docker -y
sudo systemctl start docker
sudo systemctl enable docker
sudo groupadd docker
sudo usermod -aG docker ec2-user
配置ecr凭证助手
https://github.com/awslabs/amazon-ecr-credential-helper
sudo yum install amazon-ecr-credential-helper
vim ~/.docker/config.json
{
"credsStore": "ecr-login"
}
或者手动临时配置凭证
aws ecr get-login-password --region cn-north-1 | docker login --username AWS --password-stdin xxxxxx.dkr.ecr.cn-north-1.amazonaws.com.cn
编写web服务
使用golang原生的http包实现,比较简单粗糙,测试用的话足够了
前端页面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>ToImage</title>
<style>
* {
margin: 0;
padding: 0;
}
.container {
height: 300px;
width: 100%;
margin: 100px auto 0 auto;
}
.parent {
position: relative;
top: 50%;
left: 30%;
}
.search {
width: 650px;
height: 50px;
border-radius: 18px;
outline: none;
border: 1px solid #ccc;
padding-left: 20px;
padding-right: 100px;
position: absolute;
font-size: 20px;
}
.btn {
height: 36px;
width: 100px;
position: absolute;
background: #fff;
top: 6px;
left: 650px;
border: none;
outline: none;
font-size: 20px;
}
</style>
</head>
<body>
<div class="container">
<form class="parent" method="get" action="/myrepo">
<input type="text" class="search" placeholder="Please Input..." name="iamgename" />
<input type="submit" class="btn" value="Search" />
</form>
</div>
</body>
</html>
后端服务
package main
import (
"context"
"fmt"
"io"
"log"
"net/http"
"os/exec"
"strings"
"text/template"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/awserr"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/ecr"
"github.com/aws/aws-sdk-go/service/sts"
"github.com/docker/docker/api/types"
"github.com/docker/docker/client"
)
func main() {
http.HandleFunc("/", index)
http.HandleFunc("/myrepo", myrepo)
log.Println("The server is listening on 0.0.0.0:8089")
http.ListenAndServe(":8089", nil)
}
func index(w http.ResponseWriter, r *http.Request) {
t, _ := template.ParseFiles("index.html")
t.Execute(w, "hi")
}
func myrepo(w http.ResponseWriter, r *http.Request) {
imagename := r.URL.Query().Get("iamgename")
if imagename == "" {
fmt.Fprintln(w, "Please input image name and tag")
return
}
// imagename := "public.ecr.aws/docker/library/busybox:uclibc"
// get account id
stssvc := sts.New(session.New(), aws.NewConfig().WithRegion("cn-north-1"))
input := &sts.GetCallerIdentityInput{}
stsresult, err := stssvc.GetCallerIdentity(input)
if err != nil {
fmt.Fprintln(w, "Can not get account id")
log.Println("Can not get account id")
return
}
accountid := *stsresult.Account
log.Println("Your account is", accountid)
ctx := context.Background()
cli, err := client.NewClientWithOpts(client.WithVersion("1.41"))
if err != nil {
fmt.Fprintln(w, "Unable to create docker client")
fmt.Println("Unable to create docker client")
return
}
// list images
images, err := cli.ImageList(ctx, types.ImageListOptions{})
if err != nil {
fmt.Fprintln(w, "Unable to list images")
return
}
for _, image := range images {
if imagename == image.RepoTags[0] {
fmt.Fprintln(w, "The image has existed on the host!")
fmt.Fprintln(w, "The image name is", imagename)
}
}
// pull image
pullReader, err := cli.ImagePull(ctx, imagename, types.ImagePullOptions{})
if err != nil {
panic(err)
}
defer pullReader.Close()
io.Copy(w, pullReader)
//tag image
arr := strings.Split(imagename, "/")
arrlen := len(arr)
tempstr := arr[arrlen-1]
split := strings.Split(tempstr, ":")
splen := len(split)
var tag string
var shortname string
if splen == 2 {
shortname = split[0]
tag = split[1]
} else {
shortname = split[0]
tag = "latest"
}
tagimage := accountid + ".dkr.ecr.cn-north-1.amazonaws.com.cn/" + shortname + ":" + tag
log.Println("The image name is", tagimage)
cli.ImageTag(ctx, imagename, tagimage)
// create repo
mySession := session.Must(session.NewSession())
ecrsvc := ecr.New(mySession, aws.NewConfig().WithRegion("cn-north-1"))
ecrinput := &ecr.CreateRepositoryInput{
RepositoryName: aws.String(shortname),
}
ecrresult, err := ecrsvc.CreateRepository(ecrinput)
if err != nil {
if aerr, ok := err.(awserr.Error); ok {
switch aerr.Code() {
case ecr.ErrCodeRepositoryAlreadyExistsException:
log.Println(ecr.ErrCodeRepositoryAlreadyExistsException, aerr.Error())
default:
log.Println(aerr.Error())
}
} else {
log.Println(err.Error())
}
} else {
log.Println("Success creating repo", ecrresult.Repository.RepositoryArn)
}
log.Println("The repo name is", accountid+".dkr.ecr.cn-north-1.amazonaws.com.cn/"+shortname)
// push image
// cmd := exec.Command("docker", "push", tagimage)
cmd := exec.Command("docker", "push", tagimage)
cmdout, cmderr := cmd.CombinedOutput()
if cmderr != nil {
fmt.Fprint(w, cmderr.Error())
log.Printf("docker push failed with %s\n", cmderr)
return
}
fmt.Fprint(w, string(cmdout))
// log.Println(string(cmdout))
// remove image
_, removeerr := cli.ImageRemove(ctx, tagimage, types.ImageRemoveOptions{})
if removeerr != nil {
log.Println("Failed to remover image", err)
}
// return info
fmt.Fprintln(w, "The image is successful pushed!")
fmt.Fprintln(w, "The image name is", tagimage)
fmt.Fprintln(w, "login with")
fmt.Fprintf(w, "aws ecr get-login-password --region cn-north-1 | docker login --username AWS --password-stdin %s.dkr.ecr.cn-north-1.amazonaws.com.cn", accountid)
}
测试推送镜像,推送到默认凭证所在的账号下,可以通过sts查看
{"status":"Pulling from bitnami/envoy","id":"latest"}
{"status":"Digest: sha256:1848240b060c9bad9c34f76fc87830da317628e6f7809aede6e634afa74913dd"}
{"status":"Status: Image is up to date for public.ecr.aws/bitnami/envoy:latest"}
The push refers to repository [xxxxxxxxx.dkr.ecr.cn-north-1.amazonaws.com.cn/envoy]
afe7743db796: Preparing
afe7743db796: Pushed
latest: digest: sha256:d5199964f061f4c28606f3bc8d867b12cdbb5e67af9a58821a9dacf2abd903d8 size: 529
The image is successful pushed!
The image name is xxxxxxxxx.dkr.ecr.cn-north-1.amazonaws.com.cn/envoy:latest
login with
aws ecr get-login-password --region cn-north-1 | docker login --username AWS --password-stdin xxxxxxxxx.dkr.ecr.cn-north-1.amazonaws.com.cn