diff --git a/language/golang/build-images.md b/language/golang/build-images.md index 59eef208bf..d81b8ab2d9 100644 --- a/language/golang/build-images.md +++ b/language/golang/build-images.md @@ -8,53 +8,46 @@ redirect_from: {% include_relative nav.html selected="1" %} -## Prerequisites - -* Some understanding of Go and its toolchain. This is not a tutorial on Go. If - you are new to the language, the [Go website](https://golang.org/){: target="_blank" rel="noopener" class="_"} - is a good starting point, so go (pun intended) check it out. -* You understand basic [Docker concepts](../../get-started/overview.md). -* You're familiar with the [Dockerfile format](../../build/building/packaging.md#dockerfile). -* You have [enabled BuildKit](../../build/buildkit/index.md#getting-started) - on your machine. - - ## Overview -Now that we have a good overview of containers and the Docker platform, let’s -take a look at building our first image. An image includes everything you need -to run an application – the code or binary, runtime, dependencies, and any other -file system objects required. +In this section we are going to build a container image. The image includes everything you need +to run your application – the compiled application binary file, the runtime, the libraries, and +all other resources required by your application. + +## Required software To complete this tutorial, you need the following: -- Go version 1.16 or later. You might want to [download and install Go](https://golang.org/dl/) first. -- Docker running locally. Follow the instructions to [download and install Docker](../../desktop/index.md). -- An IDE or a text editor to edit files. We recommend using [Visual Studio Code](https://code.visualstudio.com/){: target="_blank" rel="noopener" class="_"}. +- Go version 1.19 or later. Visit the [download page for Go](https://golang.org/dl/){:target="_blank" rel="noopener" class="_"} first and install the toolchain. +- Docker running locally. Follow the [instructions to download and install Docker](../../desktop/index.md). +- An IDE or a text editor to edit files. [Visual Studio Code](https://code.visualstudio.com/){: target="_blank" rel="noopener" class="_"} is a free and popular choice but you can use anything you feel comfortable with. +- A Git client. We'll use a command-line based `git` client throughout this module, but you are free to use whatever works for you. +- A command-line terminal application. The examples shown in this module are from the Linux shell, but they should work in PowerShell, Windows Command Prompt, or OS X Terminal with minimal, if any, modifications. ## Meet the example application -To avoid losing focus on Docker's features, the sample application is a minimal -HTTP server that has only three features: +The example application is a *caricature* of a microservice. It is purposefully trivial to keep focus on learning the basics of containerization for Go applications. -* It responds with a text message containing a heart symbol ("<3") on requests to `/`. -* It responds with `{"Status" : "OK"}` JSON to the health check request on requests to `/ping`. -* The port it listens on is configurable using the environment variable `HTTP_PORT`. The default value is `8080`. +The application offers two HTTP endpoints: -Thus, it somewhat mimics enough basic properties of a REST microservice to be -useful for our learning of Docker. +* It responds with a string containing a heart symbol (`<3`) to requests to `/`. +* It responds with `{"Status" : "OK"}` JSON to a request to `/health`. -The source code for the application is in the [github.com/olliefr/docker-gs-ping](https://github.com/olliefr/docker-gs-ping){: target="_blank" rel="noopener" class="_"} -GitHub repository. Please feel free to clone or fork it. +It responds with HTTP error 404 to any other request. -For our present study, we clone it to our local machine: +The application listens on a TCP port defined by the value of environment variable `PORT`. The default value is `8080`. + +The application is *stateless*. + +The complete source code for the application is on GitHub: [github.com/olliefr/docker-gs-ping](https://github.com/olliefr/docker-gs-ping){: target="_blank" rel="noopener" class="_"}. You are encouraged to fork it and experiment with it as much as you like. + +To continue, we clone the application repository to our local machine: ```console $ git clone https://github.com/olliefr/docker-gs-ping ``` -The application's `main.go` file is fairly straightforward, if you are familiar -with Go: +The application's `main.go` file is fairly straightforward, if you are familiar with Go: {% raw %} ```go @@ -79,17 +72,26 @@ func main() { return c.HTML(http.StatusOK, "Hello, Docker! <3") }) - e.GET("/ping", func(c echo.Context) error { + e.GET("/health", func(c echo.Context) error { return c.JSON(http.StatusOK, struct{ Status string }{Status: "OK"}) }) - httpPort := os.Getenv("HTTP_PORT") + httpPort := os.Getenv("PORT") if httpPort == "" { httpPort = "8080" } e.Logger.Fatal(e.Start(":" + httpPort)) } + +// Simple implementation of an integer minimum +// Adapted from: https://gobyexample.com/testing-and-benchmarking +func IntMin(a, b int) int { + if a < b { + return a + } + return b +} ``` {% endraw %} @@ -110,7 +112,7 @@ the banner, as illustrated in the next figure. ____ __ / __/___/ / ___ / _// __/ _ \/ _ \ -/___/\__/_//_/\___/ v4.2.2 +/___/\__/_//_/\___/ v4.10.2 High performance, minimalist Go web framework https://echo.labstack.com ____________________________________O/_______ @@ -118,58 +120,61 @@ ____________________________________O/_______ ⇨ http server started on [::]:8080 ``` -Let's run a quick _smoke test_ on the application. **In a new terminal**, run a -request using `curl`. Alternatively, you can use your favourite web browser as -well. +Let's run a quick _smoke test_ by accessing the application on `http://localhost:8080`. +You can use your favourite web browser, or even a `curl` command in the terminal: ```console $ curl http://localhost:8080/ Hello, Docker! <3 ``` -So, the application responds with a greeting, just as the first business -requirement says it should. Great. +This verifies that the application builds locally and we can start it without an error. +That's a milestone to celebrate! -Having established that the server is running and is accessible, let's proceed -to "dockerizing" it. +Now we are ready to "containerize" it. ## Create a Dockerfile for the application -Next, we need to add a line in our Dockerfile that tells Docker what base image -we would like to use for our application. +To build a container image with Docker, a *Dockerfile* with build instructions is required. + +We begin our `Dockerfile` with the (optional) parser directive line that instructs BuildKit to +interpret our file according to the grammar rules for the specified version of the syntax. + +We then tell Docker what *base image* we would like to use for our application: ```dockerfile # syntax=docker/dockerfile:1 -FROM golang:1.16-alpine +FROM golang:1.19 ``` Docker images can be inherited from other images. Therefore, instead of creating -our own base image, we’ll use the official Go image that already has all the tools -and packages to compile and run a Go application. You can think of this in the -same way you would think about class inheritance in object oriented programming -or functional composition in functional programming. - -When we have used that `FROM` command, we told Docker to include in our image all the functionality from the `golang:1.16-alpine` image. All of our consequent commands would build on top of that "base" image. +our own base image from scratch, we can use the official Go image that already has +all necessary tools and libraries to compile and run a Go application. > **Note** > -> If you want to learn more about creating your own base images, see [creating base images](../../build/building/base-images.md) section of the guide. +> If you are curious about creating your own base images, you can check out the following section of this guide: [creating base images](../../build/building/base-images.md). +> Note, however, that this is not necessary to continue with our task at hand. + +Now that we have defined the "base" image for our upcoming container image, +we can begin building on top of it. To make things easier when running the rest of our commands, let’s create a directory _inside_ the image that we are building. This also instructs Docker to use this directory as the default _destination_ for all subsequent commands. -This way we do not have to type out full file paths but can use relative paths -based on this directory. +This way we do not have to type out full file paths in the `Dockerfile`, +the relative paths will be based on this directory. ```dockerfile WORKDIR /app ``` Usually the very first thing you do once you’ve downloaded a project written in -Go is to install the modules necessary to compile it. +Go is to install the modules necessary to compile it. Note, that the base image +has the toolchain already, but our source code is not in it yet. -But before we can run `go mod download` inside our image, we need to get our +So before we can run `go mod download` inside our image, we need to get our `go.mod` and `go.sum` files copied into it. We use the `COPY` command to do this. In its simplest form, the `COPY` command takes two parameters. The first @@ -177,13 +182,21 @@ parameter tells Docker what files you want to copy into the image. The last parameter tells Docker where you want that file to be copied to. We’ll copy the `go.mod` and `go.sum` file into our project directory `/app` which, -owing to our use of `WORKDIR`, is the current directory (`.`) inside the image. +owing to our use of `WORKDIR`, is the current directory (`./`) inside the image. +Unlike some modern shells that appear to be indifferent to the use of trailing slash (`/`), +and can figure out what the user meant (most of the time), Docker's `COPY` command +is quite sensitive in its interpretation of the trailing slash. ```dockerfile -COPY go.mod ./ -COPY go.sum ./ +COPY go.mod go.sum ./ ``` +> **Notice** +> +> Please take some time to familiarise yourself with the trailing slash treatment +> by the `COPY` command: [Dockerfile reference](../../engine/reference/builder.md/#copy) +> as it might otherwise trick you up in more ways than you can imagine. + Now that we have the module files inside the Docker image that we are building, we can use the `RUN` command to execute the command `go mod download` there as well. This works exactly the same as if we were running `go` locally on our @@ -194,9 +207,8 @@ inside the image. RUN go mod download ``` -At this point, we have an image that is based on Go environment version 1.16 -(or a later minor version, since we had specified `1.16` as our tag in the -`FROM` command) and we have installed our dependencies. +At this point, we have a Go toolchain version 1.19.x and all our Go dependencies +installed inside the image. The next thing we need to do is to copy our source code into the image. We’ll use the `COPY` command just like we did with our module files before. @@ -213,7 +225,7 @@ Now, we would like to compile our application. To that end, we use the familiar `RUN` command: ```dockerfile -RUN go build -o /docker-gs-ping +RUN CGO_ENABLED=0 GOOS=linux go build -o /docker-gs-ping ``` This should be familiar. The result of that command will be a static application @@ -237,45 +249,48 @@ Here's the complete `Dockerfile`: ```dockerfile # syntax=docker/dockerfile:1 -FROM golang:1.16-alpine +FROM golang:1.19 +# Set destination for COPY WORKDIR /app -COPY go.mod ./ -COPY go.sum ./ +# Download Go modules +COPY go.mod go.sum ./ RUN go mod download +# Copy the source code. Note the slash at the end, as explained in +# https://docs.docker.com/engine/reference/builder/#copy COPY *.go ./ -RUN go build -o /docker-gs-ping +# Build +RUN CGO_ENABLED=0 GOOS=linux go build -o /docker-gs-ping +# Optional: +# To bind to a TCP port, runtime parameters must be supplied to the docker command. +# But we can document in the Dockerfile what ports +# the application is going to listen on by default. +# https://docs.docker.com/engine/reference/builder/#expose EXPOSE 8080 +# Run CMD [ "/docker-gs-ping" ] ``` The `Dockerfile` may also contain _comments_. They always begin with a `#` symbol, and must be at the beginning of a line. Comments are there for your convenience -to allow documenting your `Dockerfile`. Dockerfile _directives_, such as the -`syntax` directive we added, must always be at the very top of the `Dockerfile`, -so when adding comments, make sure they are after those directives: +to allow documenting your `Dockerfile`. + +There is also a concept of Dockerfile _directives_, such as the `syntax` directive we added. +The directives must always be at the very top of the `Dockerfile`, so when adding comments, +make sure that the comments follow *after* any directives that you may have used: ```dockerfile # syntax=docker/dockerfile:1 +# A sample microservice in Go packaged into a container image. -# Alpine is chosen for its small footprint -# compared to Ubuntu -FROM golang:1.16-alpine +FROM golang:1.19 -WORKDIR /app - -# Download necessary Go modules -COPY go.mod ./ -COPY go.sum ./ -RUN go mod download - -# ... the rest of the Dockerfile is ... -# ... omitted from this example ... +# ... ``` ## Build the image @@ -293,32 +308,39 @@ Let's build our first Docker image! ```console $ docker build --tag docker-gs-ping . +``` -[+] Building 3.6s (12/12) FINISHED - => [internal] load build definition from Dockerfile 0.1s - => => transferring dockerfile: 38B 0.0s - => [internal] load .dockerignore 0.1s - => => transferring context: 2B 0.0s - => [internal] load metadata for docker.io/library/golang:1.16-alpine 3.0s - => [1/7] FROM docker.io/library/golang:1.16-alpine@sha256:49c07aa83790aca732250c2258b59 0.0s - => => resolve docker.io/library/golang:1.16-alpine@sha256:49c07aa83790aca732250c2258b59 0.0s - => [internal] load build context 0.1s - => => transferring context: 114B 0.0s - => CACHED [2/7] WORKDIR /app 0.0s - => CACHED [3/7] COPY go.mod . 0.0s - => CACHED [4/7] COPY go.sum . 0.0s - => CACHED [5/7] RUN go mod download 0.0s - => CACHED [6/7] COPY *.go . 0.0s - => CACHED [7/7] RUN go build -o /docker-gs-ping 0.0s - => exporting to image 0.1s - => => exporting layers 0.0s - => => writing image sha256:336a3f164d0f079f2e42cd1d38f24ab9110d47d481f1db7f2a0b0d2859ec 0.0s - => => naming to docker.io/library/docker-gs-ping 0.0s +The build process will print some diagnostic messages as it goes through the build steps. +The following is just an example of what these messages may look like. + +```console +[+] Building 2.2s (15/15) FINISHED + => [internal] load build definition from Dockerfile 0.0s + => => transferring dockerfile: 701B 0.0s + => [internal] load .dockerignore 0.0s + => => transferring context: 2B 0.0s + => resolve image config for docker.io/docker/dockerfile:1 1.1s + => CACHED docker-image://docker.io/docker/dockerfile:1@sha256:39b85bbfa7536a5feceb7372a0817649ecb2724562a38360f4d6a7782a409b14 0.0s + => [internal] load build definition from Dockerfile 0.0s + => [internal] load .dockerignore 0.0s + => [internal] load metadata for docker.io/library/golang:1.19 0.7s + => [1/6] FROM docker.io/library/golang:1.19@sha256:5d947843dde82ba1df5ac1b2ebb70b203d106f0423bf5183df3dc96f6bc5a705 0.0s + => [internal] load build context 0.0s + => => transferring context: 6.08kB 0.0s + => CACHED [2/6] WORKDIR /app 0.0s + => CACHED [3/6] COPY go.mod go.sum ./ 0.0s + => CACHED [4/6] RUN go mod download 0.0s + => CACHED [5/6] COPY *.go ./ 0.0s + => CACHED [6/6] RUN CGO_ENABLED=0 GOOS=linux go build -o /docker-gs-ping 0.0s + => exporting to image 0.0s + => => exporting layers 0.0s + => => writing image sha256:ede8ff889a0d9bc33f7a8da0673763c887a258eb53837dd52445cdca7b7df7e3 0.0s + => => naming to docker.io/library/docker-gs-ping 0.0s ``` Your exact output will vary, but provided there aren't any errors, you should -see the `FINISHED` line in the build output. This means Docker has successfully -built our image and assigned a `docker-gs-ping` tag to it. +see the word `FINISHED` in the first line of output. This means Docker has successfully +built our image named `docker-gs-ping`. ## View local images @@ -332,13 +354,14 @@ To list images, run the `docker image ls`command (or the `docker images` shortha ```console $ docker image ls -REPOSITORY TAG IMAGE ID CREATED SIZE -docker-gs-ping latest 336a3f164d0f 39 minutes ago 540MB -postgres 13.2 c5ec7353d87d 7 weeks ago 314MB +REPOSITORY TAG IMAGE ID CREATED SIZE +docker-gs-ping latest 7f153fbcc0a8 2 minutes ago 1.11GB +... ``` -Your exact output may vary, but you should see `docker-gs-ping` image with the -`latest` tag. +Your exact output may vary, but you should see the `docker-gs-ping` image with the +`latest` tag. Because we had not specified a custom tag when we built our image, +Docker assumed that the tag would be `latest`, which is a special value. ## Tag images @@ -372,10 +395,10 @@ images: ```console $ docker image ls -REPOSITORY TAG IMAGE ID CREATED SIZE -docker-gs-ping latest 336a3f164d0f 43 minutes ago 540MB -docker-gs-ping v1.0 336a3f164d0f 43 minutes ago 540MB -postgres 13.2 c5ec7353d87d 7 weeks ago 314MB +REPOSITORY TAG IMAGE ID CREATED SIZE +docker-gs-ping latest 7f153fbcc0a8 6 minutes ago 1.11GB +docker-gs-ping v1.0 7f153fbcc0a8 6 minutes ago 1.11GB +... ``` You can see that we have two images that start with `docker-gs-ping`. We know @@ -392,14 +415,20 @@ $ docker image rm docker-gs-ping:v1.0 Untagged: docker-gs-ping:v1.0 ``` -Notice that the response from Docker tells us that the image has not been removed but only "untagged". Verify this by running the images command: +Notice that the response from Docker tells us that the image has not been removed but only "untagged". + +Verify this by running the following command: ```console $ docker image ls +``` -REPOSITORY TAG IMAGE ID CREATED SIZE -docker-gs-ping latest 336a3f164d0f 45 minutes ago 540MB -postgres 13.2 c5ec7353d87d 7 weeks ago 314MB +You will see that the tag `v1.0` is no longer in the list of images kept by your Docker instance. + +``` +REPOSITORY TAG IMAGE ID CREATED SIZE +docker-gs-ping latest 7f153fbcc0a8 7 minutes ago 1.11GB +... ``` The tag `v1.0` has been removed but we still have the `docker-gs-ping:latest` @@ -407,17 +436,22 @@ tag available on our machine, so the image is there. ## Multi-stage builds -You may have noticed that our `docker-gs-ping` image stands at 540MB, which you -may think is a lot. You may also be wondering whether our dockerized application -still needs the full suite of Go tools, including the compiler, after the -application binary had been compiled. +You may have noticed that our `docker-gs-ping` image weighs in at over a gigabyte (!!!), +which is *a lot* for a tiny compiled Go application. You may also be wondering what happened +to the full suite of Go tools, including the compiler, after we had built our image. -These are legit concerns. Both can be solved by using _multi-stage builds_. The -following example is provided with little explanation because this would derail -us from our current concerns, but please feel free to explore on your own later. -The main idea is that we use one image to produce some artifacts, which are then -placed into another, much smaller image, containing only the parts necessary for -running the artifacts that we'd built. +The answer is that the full toolchain is still there, in the container image. +Not only this is inconvenient because of the large file size, but it may also +present a security risk when the container is deployed. + +These two issues can be solved by using [multi-stage builds](../../build/building/multi-stage.md). + +In a nutshell, a multi-stage build can carry over the artifacts from one build stage into another, +and every build stage can be instantiated from a different base image. + +Thus, in the following example, we are going to use a full-scale official Go image to build +our application but then we'll copy the application binary into another image whose base +is very lean and does not include the Go toolchain or other optional components. The `Dockerfile.multistage` in the sample application's repo has the following content: @@ -426,25 +460,28 @@ content: ```dockerfile # syntax=docker/dockerfile:1 -## Build -FROM golang:1.16-buster AS build +# Build the application from source +FROM golang:1.19 AS build-stage WORKDIR /app -COPY go.mod ./ -COPY go.sum ./ +COPY go.mod go.sum ./ RUN go mod download COPY *.go ./ -RUN go build -o /docker-gs-ping +RUN CGO_ENABLED=0 GOOS=linux go build -o /docker-gs-ping -## Deploy -FROM gcr.io/distroless/base-debian10 +# Run the tests in the container +FROM build-stage AS run-test-stage +RUN go test -v ./... + +# Deploy the application binary into a lean image +FROM gcr.io/distroless/base-debian11 AS build-release-stage WORKDIR / -COPY --from=build /docker-gs-ping /docker-gs-ping +COPY --from=build-stage /docker-gs-ping /docker-gs-ping EXPOSE 8080 @@ -454,42 +491,36 @@ ENTRYPOINT ["/docker-gs-ping"] ``` {% endraw %} -Since we have two dockerfiles now, we have to tell Docker that we want to build -using our new Dockerfile. We also tag the new image with `multistage` but this -word has no special meaning, we only do so that we could compare this new image -to the one we've built previously, that is the one we tagged with `latest`: +Since we have two Dockerfiles now, we have to tell Docker what Dockerfile we'd like to use +to build the image. Let's tag the new image with `multistage`. This tag (like any other, +apart from `latest`) has no special meaning for Docker, it's just something we chose. ```console $ docker build -t docker-gs-ping:multistage -f Dockerfile.multistage . ``` Comparing the sizes of `docker-gs-ping:multistage` and `docker-gs-ping:latest` -we see an order-of-magnitude difference! - -```console -$ docker image ls +we see a *few orders-of-magnitude* difference! (`docker image ls`) +``` REPOSITORY TAG IMAGE ID CREATED SIZE -docker-gs-ping multistage e3fdde09f172 About a minute ago 27.1MB -docker-gs-ping latest 336a3f164d0f About an hour ago 540MB +docker-gs-ping multistage e3fdde09f172 About a minute ago 28.1MB +docker-gs-ping latest 336a3f164d0f About an hour ago 1.11GB ``` -This is due to the fact that the ["distroless" base image](https://github.com/GoogleContainerTools/distroless){:target="_blank" rel="noopener" class="_"} -that we have used to deploy our Go application is very barebones and is meant -for lean deployments of static binaries. +This is so because the ["distroless"](https://github.com/GoogleContainerTools/distroless){:target="_blank" rel="noopener" class="_"} +base image that we have used in the second stage of the build is very barebones and is designed for lean deployments of static binaries. -For more information on multi-stage builds, please feel free to check out -[other parts](../../build/building/multi-stage.md) of the Docker -documentation. This is, however, not essential for our progress here, so we'll +There's much more to multi-stage builds, including the possibility of multi-architecture builds, +so please feel free to check out the [multi-stage builds](../../build/building/multi-stage.md) +section of Docker documentation. This is, however, not essential for our progress here, so we'll leave it at that. ## Next steps -In this module, we took a look at setting up our example Go application that we -will use for much of the rest of the tutorial. We also created a `Dockerfile` -that we used to build our Docker image. Then, we took a look at tagging our -images and removing images and tags. In the next module, we’ll take a look at -how to: +In this module, we met our example application and built and container image for it. + +In the next module, we’ll take a look at how to: [Run your image as a container](run-containers.md){: .button .outline-btn} diff --git a/language/golang/index.md b/language/golang/index.md index f2b8c0ad40..69430698a0 100644 --- a/language/golang/index.md +++ b/language/golang/index.md @@ -6,25 +6,41 @@ toc_min: 1 toc_max: 2 --- -In this guide, you will learn how to create a containerized Go application using Docker. - -Why [Go](https://golang.org/){:target="_blank" rel="noopener" class="_"}? Go is an open-source programming language that lets you build simple, reliable, and efficient software. Go is undeniably a major player in the modern Cloud ecosystem; both Docker and Kubernetes are written in Go. - -[golang]: https://golang.org/ +This guide will show you how to create, test, and deploy containerized Go applications using Docker. > **Acknowledgment** > -> We'd like to thank [Oliver Frolovs](https://twitter.com/nocturnalgopher){:target="_blank" rel="noopener" class="_"} for his contribution to the Golang get started guide. +> We'd like to thank [Oliver Frolovs](https://www.linkedin.com/in/ofr/){:target="_blank" rel="noopener" class="_"} for his contribution to this guide. + +## What will you learn? In this guide, you’ll learn how to: -* Create a new `Dockerfile` which contains instructions required to build a Docker image for a simple Go program -* Run the newly built image as a container -* Set up a local development environment to connect a database to the container -* Use Docker Compose to run your Go application and other services it requires +* Create a *Dockerfile* which contains the instructions for building a container image for a program written in Go. +* Run the image as a container in your local Docker instance and manage the container's lifecycle. +* Use multi-stage builds for building small images efficiently while keeping your *Dockerfiles* easy to read and maintain. +* Use Docker Compose to orchestrate running of multiple related containers together in a development environment. * Configure a CI/CD pipeline for your application using [GitHub Actions](https://docs.github.com/en/actions){:target="_blank" rel="noopener" class="_"} +* Deploy your containerized Go application to Google [Cloud Run](https://cloud.google.com/run/docs/overview/what-is-cloud-run){:target="_blank" rel="noopener" class="_"} serverless platform. -You can containerize your own Go application using the examples and resources provided after you complete the Go getting started modules. +## Prerequisites + +Some basic understanding of Go and its toolchain is assumed. This is not a Go tutorial. If you are new to the language, +the [Go website](https://golang.org/){: target="_blank" rel="noopener" class="_"} is a great place to explore, +so *go* (pun intended) check it out! + +You also must know some basic [Docker concepts](../../get-started/overview.md) as well as to +be at least vaguely familiar with the [Dockerfile format](../../build/building/packaging.md#dockerfile). + +Your Docker set-up must have BuildKit enabled. BuildKit is enabled by default for all users on [Docker Desktop](../../desktop/index.md). +If you have installed Docker Desktop, you don’t have to manually enable BuildKit. If you are running Docker on Linux, +please check out BuildKit [getting started](../../build/buildkit/index.md#getting-started) page. + +Some familiarity with the command line is also expected. + +## What's next? + +The aim of this guide is to provide enough examples and instructions for you to containerize your own Go application and deploy it into the Cloud. Let's get started! diff --git a/language/golang/run-containers.md b/language/golang/run-containers.md index bd8f048567..0acfa41618 100644 --- a/language/golang/run-containers.md +++ b/language/golang/run-containers.md @@ -28,7 +28,7 @@ $ docker run docker-gs-ping ____ __ / __/___/ / ___ / _// __/ _ \/ _ \ -/___/\__/_//_/\___/ v4.2.2 +/___/\__/_//_/\___/ v4.10.2 High performance, minimalist Go web framework https://echo.labstack.com ____________________________________O/_______