Dockerソース分析2:Docker Client
23468 ワード
Docker Clientは、Dockerアーキテクチャにおいて、ユーザがDocker Daemonと通信を確立するクライアントである.この文書では、Docker 18.02.0-ceのソースコードに基づいてDocker Clientの内容を分析する.主に次の2つのセクションがあります. Docker Clientの作成 Docker Clientコマンドの実行 1、Docker Clientの作成
DockerのようなC/Sアーキテクチャの場合、クライアントの存在はDockerの対応するタスクの開始を意味する.ユーザーはまずDocker Clientを作成し、特定の要求タイプとパラメータをDocker Clientに渡し、最終的にDocker ClientからDocker Serverが認識できる形式に変換し、Docker Serverに送信します.Docker Clientのmain関数はdocker-ce/components/cli/cmd/docker/dockerにあります.go、コードは以下の通りです.
1)NewDockerCliメソッドはdocker-ce/components/cli/cli/command/cliにある.goファイル.DockerCliタイプのオブジェクトインスタンスを返します.コードは次のとおりです.
2)newDockerCommandはmain関数と同じdocker-ce/components/cli/cmd/docker/docker.goファイルでは、DockerCliタイプのオブジェクトインスタンスに基づいてcobraを返します.Commandオブジェクトインスタンス.まず、いくつかのライブラリについて説明します.
新DockerCommandのコードは次のとおりです.
NewClientOptionsは、clientのoptionsを構成するオブジェクトを返します.
3)cmdを実行し,エラー情報処理を行う.これでDocker Clientが作成されます.
2、Docker Clientによる命令の実行
第一小結ではcommands.AddCommands(cmd,dockerCli)はdockerルートコマンドにすべてのサブコマンドを追加します.AddCommandsメソッドはdocker-ce/components/cli/cli/command/commandsにあります.コードは次のとおりです.
以下、Pullイメージを例にDocker Clientによるコマンドの実行について説明する.image.NewImageCommand(dockerCli)メソッドはdocker-ce/components/cli/cli/command/image/cmdにある.goファイルにあります.コードは次のとおりです.
NewPullCommandメソッドを詳しく見てみましょう.docker-ce/components/cli/cli/command/image/pullにあります.goファイル:
ミラー・プルの具体的なコードは次のとおりです.
ImagePullメソッドはdocker-ce/components/engine/client/image_にあります.pull.goファイルでは、次のようになります.
tryImageCreateメソッドはdocker-ce/components/engine/client/image_にあります.create.goファイルでは、次のようになります.
以上がpullリクエストのすべてのプロセスであり,他のリクエストは実行プロセスにおいても大きく異なる.要するに、要求実行中は、コマンドラインの要求に関するパラメータを初期処理し、対応する補助情報を追加し、最終的に指定されたプロトコルを介してDockerサーバに対応するAPI要求を送信することが多い.これらのAPIリクエストは、Docker ClientとDocker Serverが事前に約束したものです.要求の具体的な応答、すなわち本当の意味でのミラーダウンロードを実現するには、Dockerソース分析3:Dockerサーバで詳細に説明します.
DockerのようなC/Sアーキテクチャの場合、クライアントの存在はDockerの対応するタスクの開始を意味する.ユーザーはまずDocker Clientを作成し、特定の要求タイプとパラメータをDocker Clientに渡し、最終的にDocker ClientからDocker Serverが認識できる形式に変換し、Docker Serverに送信します.Docker Clientのmain関数はdocker-ce/components/cli/cmd/docker/dockerにあります.go、コードは以下の通りです.
func main() {
// Set terminal emulation based on platform as required.
stdin, stdout, stderr := term.StdStreams()
logrus.SetOutput(stderr)
// Cli, DockerCli
dockerCli := command.NewDockerCli(stdin, stdout, stderr)
// cmd,
cmd := newDockerCommand(dockerCli)
// cmd
if err := cmd.Execute(); err != nil {
if sterr, ok := err.(cli.StatusError); ok {
if sterr.Status != "" {
fmt.Fprintln(stderr, sterr.Status)
}
// StatusError should only be used for errors, and all errors should
// have a non-zero exit status, so never exit with 0
if sterr.StatusCode == 0 {
os.Exit(1)
}
os.Exit(sterr.StatusCode)
}
fmt.Fprintln(stderr, err)
os.Exit(1)
}
}
1)NewDockerCliメソッドはdocker-ce/components/cli/cli/command/cliにある.goファイル.DockerCliタイプのオブジェクトインスタンスを返します.コードは次のとおりです.
// DockerCli is an instance the docker command line client.
// Instances of the client can be returned from NewDockerCli.
type DockerCli struct {
configFile *configfile.ConfigFile
in *InStream
out *OutStream
err io.Writer
client client.APIClient
serverInfo ServerInfo
clientInfo ClientInfo
}
// ServerInfo stores details about the supported features and platform of the server
type ServerInfo struct {
HasExperimental bool
OSType string
}
// ClientInfo stores details about the supported features of the client
type ClientInfo struct {
HasExperimental bool
DefaultVersion string //api.defaultVersion or DOCKER_API_VERSION
Orchestrator Orchestrator
}
// NewDockerCli returns a DockerCli instance with IO output and error streams set by in, out and err.
func NewDockerCli(in io.ReadCloser, out, err io.Writer) *DockerCli {
return &DockerCli{in: NewInStream(in), out: NewOutStream(out), err: err} // DockerCli
}
2)newDockerCommandはmain関数と同じdocker-ce/components/cli/cmd/docker/docker.goファイルでは、DockerCliタイプのオブジェクトインスタンスに基づいてcobraを返します.Commandオブジェクトインスタンス.まず、いくつかのライブラリについて説明します.
"github.com/sirupsen/logrus" // log
"github.com/spf13/cobra" //
"github.com/spf13/pflag" // go flag
新DockerCommandのコードは次のとおりです.
// DockerCli Command.Cli
func newDockerCommand(dockerCli *command.DockerCli) *cobra.Command {
opts := cliflags.NewClientOptions() // client options
var flags *pflag.FlagSet
cmd := &cobra.Command{ // cobra.Command , docker
Use: "docker [OPTIONS] COMMAND [ARG...]",
Short: "A self-sufficient runtime for containers",
SilenceUsage: true,
SilenceErrors: true,
TraverseChildren: true,
Args: noArgs,
//
RunE: func(cmd *cobra.Command, args []string) error {
if opts.Version {
showVersion()
return nil
}
// docker ShowHelp, help
return command.ShowHelp(dockerCli.Err())(cmd, args)
},
//
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
// flags must be the top-level command flags, not cmd.Flags()
opts.Common.SetDefaultOptions(flags)
dockerPreRun(opts)
if err := dockerCli.Initialize(opts); err != nil {
return err
}
return isSupported(cmd, dockerCli)
},
}
// usage, help
cli.SetupRootCommand(cmd) //SetupRootCommand sets default usage, help, and error handling for the root command.
flags = cmd.Flags()
flags.BoolVarP(&opts.Version, "version", "v", false, "Print version information and quit")
flags.StringVar(&opts.ConfigDir, "config", cliconfig.Dir(), "Location of client config files")
opts.Common.InstallFlags(flags)
setFlagErrorFunc(dockerCli, cmd, flags, opts)
setHelpFunc(dockerCli, cmd, flags, opts)
cmd.SetOutput(dockerCli.Out())
// ( docker run、docker build )
commands.AddCommands(cmd, dockerCli) // AddCommands adds all the commands from cli/command to the root command
setValidateArgs(dockerCli, cmd, flags, opts)
return cmd
}
NewClientOptionsは、clientのoptionsを構成するオブジェクトを返します.
// ClientOptions are the options used to configure the client cli
type ClientOptions struct {
Common *CommonOptions
ConfigDir string
Version bool
}
// NewClientOptions returns a new ClientOptions
func NewClientOptions() *ClientOptions {
return &ClientOptions{Common: NewCommonOptions()}
}
// CommonOptions are options common to both the client and the daemon.
type CommonOptions struct {
Debug bool
Hosts []string
Orchestrator string
LogLevel string
TLS bool
TLSVerify bool
TLSOptions *tlsconfig.Options
}
// NewCommonOptions returns a new CommonOptions
func NewCommonOptions() *CommonOptions {
return &CommonOptions{}
}
3)cmdを実行し,エラー情報処理を行う.これでDocker Clientが作成されます.
2、Docker Clientによる命令の実行
第一小結ではcommands.AddCommands(cmd,dockerCli)はdockerルートコマンドにすべてのサブコマンドを追加します.AddCommandsメソッドはdocker-ce/components/cli/cli/command/commandsにあります.コードは次のとおりです.
// AddCommands adds all the commands from cli/command to the root command
func AddCommands(cmd *cobra.Command, dockerCli *command.DockerCli) {
cmd.AddCommand(
// checkpoint
checkpoint.NewCheckpointCommand(dockerCli),
// config
config.NewConfigCommand(dockerCli),
// container
container.NewContainerCommand(dockerCli),
container.NewRunCommand(dockerCli),
// image
image.NewImageCommand(dockerCli),
image.NewBuildCommand(dockerCli),
// manifest
manifest.NewManifestCommand(dockerCli),
// network
network.NewNetworkCommand(dockerCli),
// node
node.NewNodeCommand(dockerCli),
// plugin
plugin.NewPluginCommand(dockerCli),
// registry
registry.NewLoginCommand(dockerCli),
registry.NewLogoutCommand(dockerCli),
registry.NewSearchCommand(dockerCli),
// secret
secret.NewSecretCommand(dockerCli),
// service
service.NewServiceCommand(dockerCli),
// system
system.NewSystemCommand(dockerCli),
system.NewVersionCommand(dockerCli),
// stack
stack.NewStackCommand(dockerCli),
stack.NewTopLevelDeployCommand(dockerCli),
// swarm
swarm.NewSwarmCommand(dockerCli),
// trust
trust.NewTrustCommand(dockerCli),
// volume
volume.NewVolumeCommand(dockerCli),
// legacy commands may be hidden
// DOCKER_HIDE_LEGACY_COMMANDS , DOCKER_HIDE_LEGACY_COMMANDS=true docker --help
hide(system.NewEventsCommand(dockerCli))
...
)
}
以下、Pullイメージを例にDocker Clientによるコマンドの実行について説明する.image.NewImageCommand(dockerCli)メソッドはdocker-ce/components/cli/cli/command/image/cmdにある.goファイルにあります.コードは次のとおりです.
// NewImageCommand returns a cobra command for `image` subcommands
func NewImageCommand(dockerCli command.Cli) *cobra.Command {
cmd := &cobra.Command{
Use: "image",
Short: "Manage images",
Args: cli.NoArgs,
RunE: command.ShowHelp(dockerCli.Err()),
}
cmd.AddCommand(
NewBuildCommand(dockerCli),
NewHistoryCommand(dockerCli),
NewImportCommand(dockerCli),
NewLoadCommand(dockerCli),
//
NewPullCommand(dockerCli),
NewPushCommand(dockerCli),
NewSaveCommand(dockerCli),
NewTagCommand(dockerCli),
newListCommand(dockerCli),
newRemoveCommand(dockerCli),
newInspectCommand(dockerCli),
NewPruneCommand(dockerCli),
)
return cmd
}
NewPullCommandメソッドを詳しく見てみましょう.docker-ce/components/cli/cli/command/image/pullにあります.goファイル:
type pullOptions struct {
remote string
all bool
platform string
}
// NewPullCommand creates a new `docker pull` command
func NewPullCommand(dockerCli command.Cli) *cobra.Command {
var opts pullOptions
cmd := &cobra.Command{
Use: "pull [OPTIONS] NAME[:TAG|@DIGEST]",
Short: "Pull an image or a repository from a registry",
Args: cli.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
opts.remote = args[0] // docker pull , image
return runPull(dockerCli, opts) // pull
},
}
flags := cmd.Flags()
flags.BoolVarP(&opts.all, "all-tags", "a", false, "Download all tagged images in the repository")
command.AddPlatformFlag(flags, &opts.platform)
command.AddTrustVerificationFlags(flags)
return cmd
}
// pull
func runPull(cli command.Cli, opts pullOptions) error {
//
distributionRef, err := reference.ParseNormalizedNamed(opts.remote)
switch {
case err != nil:
return err
case opts.all && !reference.IsNameOnly(distributionRef):
return errors.New("tag can't be used with --all-tags/-a")
case !opts.all && reference.IsNameOnly(distributionRef):
// , tag, latest tag
distributionRef = reference.TagNameOnly(distributionRef)
if tagged, ok := distributionRef.(reference.Tagged); ok {
fmt.Fprintf(cli.Out(), "Using default tag: %s
", tagged.Tag())
}
}
ctx := context.Background()
imgRefAndAuth, err := trust.GetImageReferencesAndAuth(ctx, AuthResolver(cli), distributionRef.String()) // retrieves the necessary reference and auth information for an image name as an ImageRefAndAuth struct
if err != nil {
return err
}
// Check if reference has a digest
_, isCanonical := distributionRef.(reference.Canonical)
if command.IsTrusted() && !isCanonical {
err = trustedPull(ctx, cli, imgRefAndAuth, opts.platform) //
} else {
//
err = imagePullPrivileged(ctx, cli, imgRefAndAuth, opts.all, opts.platform)
}
if err != nil {
if strings.Contains(err.Error(), "when fetching 'plugin'") {
return errors.New(err.Error() + " - Use `docker plugin install`")
}
return err
}
return nil
}
ミラー・プルの具体的なコードは次のとおりです.
// imagePullPrivileged pulls the image and displays it to the output
func imagePullPrivileged(ctx context.Context, cli command.Cli, imgRefAndAuth trust.ImageRefAndAuth, all bool, platform string) error {
ref := reference.FamiliarString(imgRefAndAuth.Reference())
encodedAuth, err := command.EncodeAuthToBase64(*imgRefAndAuth.AuthConfig())
if err != nil {
return err
}
requestPrivilege := command.RegistryAuthenticationPrivilegedFunc(cli, imgRefAndAuth.RepoInfo().Index, "pull")
options := types.ImagePullOptions{
RegistryAuth: encodedAuth,
PrivilegeFunc: requestPrivilege,
All: all,
Platform: platform,
}
// Client ImagePull Docker server
responseBody, err := cli.Client().ImagePull(ctx, ref, options)
if err != nil {
return err
}
defer responseBody.Close()
return jsonmessage.DisplayJSONMessagesToStream(responseBody, cli.Out(), nil)
}
ImagePullメソッドはdocker-ce/components/engine/client/image_にあります.pull.goファイルでは、次のようになります.
func (cli *Client) ImagePull(ctx context.Context, refStr string, options types.ImagePullOptions) (io.ReadCloser, error) {
ref, err := reference.ParseNormalizedNamed(refStr)
if err != nil {
return nil, err
}
query := url.Values{}
query.Set("fromImage", reference.FamiliarName(ref))
if !options.All {
query.Set("tag", getAPITagFromNamedRef(ref))
}
if options.Platform != "" {
query.Set("platform", strings.ToLower(options.Platform))
}
resp, err := cli.tryImageCreate(ctx, query, options.RegistryAuth)
if resp.statusCode == http.StatusUnauthorized && options.PrivilegeFunc != nil {
newAuthHeader, privilegeErr := options.PrivilegeFunc()
if privilegeErr != nil {
return nil, privilegeErr
}
resp, err = cli.tryImageCreate(ctx, query, newAuthHeader)
}
if err != nil {
return nil, err
}
return resp.body, nil
}
tryImageCreateメソッドはdocker-ce/components/engine/client/image_にあります.create.goファイルでは、次のようになります.
// ImageCreate creates a new image based in the parent options.
// It returns the JSON content in the response body.
func (cli *Client) ImageCreate(ctx context.Context, parentReference string, options types.ImageCreateOptions) (io.ReadCloser, error) {
ref, err := reference.ParseNormalizedNamed(parentReference)
if err != nil {
return nil, err
}
query := url.Values{}
query.Set("fromImage", reference.FamiliarName(ref))
query.Set("tag", getAPITagFromNamedRef(ref))
if options.Platform != "" {
query.Set("platform", strings.ToLower(options.Platform))
}
resp, err := cli.tryImageCreate(ctx, query, options.RegistryAuth)
if err != nil {
return nil, err
}
return resp.body, nil
}
func (cli *Client) tryImageCreate(ctx context.Context, query url.Values, registryAuth string) (serverResponse, error) {
// Docker Server POST , url /images/create, header X-Registry-Auth
headers := map[string][]string{"X-Registry-Auth": {registryAuth}}
return cli.post(ctx, "/images/create", query, nil, headers)
}
以上がpullリクエストのすべてのプロセスであり,他のリクエストは実行プロセスにおいても大きく異なる.要するに、要求実行中は、コマンドラインの要求に関するパラメータを初期処理し、対応する補助情報を追加し、最終的に指定されたプロトコルを介してDockerサーバに対応するAPI要求を送信することが多い.これらのAPIリクエストは、Docker ClientとDocker Serverが事前に約束したものです.要求の具体的な応答、すなわち本当の意味でのミラーダウンロードを実現するには、Dockerソース分析3:Dockerサーバで詳細に説明します.