This is a creation in Article, where the information may have evolved or changed.
Deploying Golang apps to Docker
Project Address: Https://github.com/EDDYCJY/go ... (Get in the car, support a wave)
Original address: Https://segmentfault.com/a/11 ...
Note:
- You need to install
docker
and match the image source before you start.
- This section of the source code on the
f-20180324-docker
branch
- Start the project catalog from this section
go-gin-example
as a benchmark (please adapt yourself to your local project)
Introduced
Here's a brief introduction to Docker, which suggests deep learning
Docker is an open-source, lightweight container technology that allows developers to package their applications and apply the context of their run to a portable image and then publish them to any Docker-enabled system. With container technology, Docker provides an isolated operating environment for applications with virtually no performance overhead
- Simplified configuration
- Code Pipeline Management
- Improve development efficiency
- Isolated Applications
- Fast, continuous deployment
We will then formally begin the docker
process and preparation of the project, with each headline as a step outline
Golang
First, the preparation of Dockerfile
go-gin-example
Create a Dockerfile file in the project root directory, write content
FROM golang:latestWORKDIR $GOPATH/src/github.com/EDDYCJY/go-gin-exampleCOPY . $GOPATH/src/github.com/EDDYCJY/go-gin-exampleRUN go build .EXPOSE 8000ENTRYPOINT ["./go-gin-example"]
Role
golang:latest
Mirror as the underlying image, set the working directory to $GOPATH/src/go-gin-example
, and copy the contents of the current context directory into the $GOPATH/src/go-gin-example
After the go build
compilation is complete, set the container launcher to the ./go-gin-example
executable file that we compiled
Note go-gin-example
that docker
compiling in the container does not compile on the host site.
Description
The Dockerfile file is a configuration file for defining the Docker image generation process, the file content is an instruction, each instruction is constructed one layer, so the content of each instruction is to describe how the layer should be constructed; These directives apply to the underlying image and eventually create a new image
You can think of to quickly create a custom Docker image
1. From
Specify the underlying image (required instruction, and must be the first instruction)
2, Workdir
Format is WORKDIR
< working directory path >
You WORKDIR
can use directives to specify the working directory (or the current directory), and the current directory for each layer is changed to the specified directory, and if the directory does not exist, it WORKDIR
will help you set up the directory
3. COPY
Format:
COPY <源路径>... <目标路径>COPY ["<源路径1>",... "<目标路径>"]
COPY
Directives will be copied from the build context directory < source path > files/directories into a new layer of mirrors within the < destination path > location
4. RUN
Used to execute command-line commands
Format: RUN
< command >
5, EXPOSE
Format EXPOSE
< port 1> [< port 2> ...]
EXPOSE
The directive is to declare the runtime container to provide the service port, which is just a declaration that will not be applied to this port at runtime because the service is opened
There are two benefits to writing such a declaration in Dockerfile
- Helps image users understand the daemon port of the mirroring service to facilitate configuration mapping
- When the runtime uses random port mappings, that is
docker run -P
, when the port is automatically mapped randomly EXPOSE
6, EntryPoint
ENTRYPOINT
In the same format as the RUN
instruction format, it is divided into two formats
<ENTRYPOINT> "<CMD>"
ENTRYPOINT [ "curl", "-s", "http://ip.cn" ]
ENTRYPOINT
Instruction is the specified container launcher and parameters
Second, build the image
go-gin-example
The project root directory is executed under thedocker build -t gin-blog-docker .
The command function is to create/build the image, -t
specify the name gin-blog-docker
, and .
build the content as the current context directory
$ docker build -t gin-blog-docker .Sending build context to Docker daemon 96.39 MBStep 1/6 : FROM golang:latest ---> d632bbfe5767Step 2/6 : WORKDIR $GOPATH/src/github.com/EDDYCJY/go-gin-example ---> 56294f978c5dRemoving intermediate container e112997b995dStep 3/6 : COPY . $GOPATH/src/github.com/EDDYCJY/go-gin-example ---> 3b60960120cfRemoving intermediate container 63e310b3f60cStep 4/6 : RUN go build . ---> Running in 52648a431450 ---> 7bfbeb301feaRemoving intermediate container 52648a431450Step 5/6 : EXPOSE 8000 ---> Running in 98f5b387d1bb ---> b65bd4076c65Removing intermediate container 98f5b387d1bbStep 6/6 : ENTRYPOINT ./go-gin-example ---> Running in c4f6cdeb667b ---> d8a109c7697cRemoving intermediate container c4f6cdeb667bSuccessfully built d8a109c7697c
Third, verify the image
View all mirrors to determine gin-blog-docker
if the image you just built is present
$ docker imagesREPOSITORY TAG IMAGE ID CREATED SIZEgin-blog-docker latest d8a109c7697c About a minute ago 946 MBdocker.io/golang latest d632bbfe5767 8 days ago 779 MB...
Iv. Creating and running a new container
Execute commanddocker run -p 8000:8000 gin-blog-docker
$ docker run -p 8000:8000 gin-blog-dockerdial tcp 127.0.0.1:3306: connect: connection refused[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production. - using env: export GIN_MODE=release - using code: gin.SetMode(gin.ReleaseMode)...Actual pid is 1
You think you're done with the operation?
You think too much, take a closer look at the console output an error dial tcp 127.0.0.1:3306: connect: connection refused
Let's try to find out what Mysql
the problem is, and then the second we'll solve the problem.
Mysql
One, pull the mirror
Docker
Download the image from the public repository Dockerhub
MySQL
(domestic recommendation with a mirror)
$ docker pull mysql
Second, create and run a new container
Run Mysql
the container and set the container ID to return after successful execution
$ docker run --name mysql -p 3306:3306 -e MYSQL_ROOT_PASSWORD=rootroot -d mysql8c86ac986da4922492934b6fe074254c9165b8ee3e184d29865921b0fef29e64
Connect to Mysql
The initialization Mysql
should
Golang + Mysql
First, remove the image
Because there is a problem with the original image, we need to delete it, here are a few ways
- Remove the original problematic image and rebuild a new image
- Rebuilding a different
name
, tag
new image
Delete the original problematic image, which -f
is forced to delete and its associated state
If you do not -f
, you need to perform a docker ps -a
check on the associated container to rm
remove the dependencies
$ docker rmi -f gin-blog-dockerUntagged: gin-blog-docker:latestDeleted: sha256:d8a109c7697c3c2d9b4de7dbb49669d10106902122817b6467a031706bc52ab4Deleted: sha256:b65bd4076c65a3c24029ca4def3b3f37001ff7c9eca09e2590c4d29e1e23dce5Deleted: sha256:7bfbeb301fea9d8912a4b7c43e4bb8b69bdc57f0b416b372bfb6510e476a7deeDeleted: sha256:3b60960120cf619181c1762cdc1b8ce318b8c815e056659809252dd321bcb642Deleted: sha256:56294f978c5dfcfa4afa8ad033fd76b755b7ecb5237c6829550741a4d2ce10bc
Second, modify the configuration file
Change the project's configuration file conf/app.ini
to
#debug or releaseRUN_MODE = debug[app]PAGE_SIZE = 10JWT_SECRET = 233[server]HTTP_PORT = 8000READ_TIMEOUT = 60WRITE_TIMEOUT = 60[database]TYPE = mysqlUSER = rootPASSWORD = rootrootHOST = mysql:3306NAME = blogTABLE_PREFIX = blog_
Iii. Rebuilding the image
Repeat the previous steps and go back to gin-blog
the project root directory to executedocker build -t gin-blog-docker .
Iv. Creating and running a new container
Association
Q: We need to Golang
correlate containers and Mysql
containers, so what do we need to do?
A: Increase the command --link mysql:mysql
to allow the Golang
container to interconnect with the container, Mysql
through --link
, can be directly within the container using its associated container alias access , not through IP, but --link
only to resolve the association between single-machine containers, in the case of distributed multi-machine, Need to connect in another way
Run
Execute commanddocker run --link mysql:mysql -p 8000:8000 gin-blog-docker
$ docker run --link mysql:mysql -p 8000:8000 gin-blog-docker[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production. - using env: export GIN_MODE=release - using code: gin.SetMode(gin.ReleaseMode)...Actual pid is 1
Results
Check the startup output, interface test, data in the database, all normal; our Golang
containers and Mysql
containers are successfully connected and running, and we're done:)
Review
Thinking
Although the application has been able to run up
But if there is a Golang
Docker
certain understanding, I hope you can think of at least 2 questions
- Why do you
gin-blog-docker
occupy so much space? (available for docker ps -as | grep gin-blog-docker
Viewing)
Mysql
Containers are used directly, where are the data stored?
Create an ultra-small golang image
Q: The first question, why is this mirror size so large?
A: The FROM golang:latest
official image is drawn, golang
including the compilation and operating environment of Golang, plus a bunch of gcc, build tools, quite complete
This is problematic, we can not compile the site in the Golang container, we do not use those things at all, we only need an environment that can run the executable file
Building Scratch Mirrors
Scratch mirror, simple, compact, basic is an empty image
I. Modification of Dockerfile
FROM scratchWORKDIR $GOPATH/src/github.com/EDDYCJY/go-gin-exampleCOPY . $GOPATH/src/github.com/EDDYCJY/go-gin-exampleEXPOSE 8000CMD ["./go-gin-example"]
Second, compile the executable file
CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o go-gin-example .
The executable generated by the compilation relies on some libraries and is dynamically linked. In this case, because the scratch
mirror is used, it is an empty image, so we need to make a static link to the generated executable that depends on the library
Third, build the image
$ docker build -t gin-blog-docker-scratch .Sending build context to Docker daemon 133.1 MBStep 1/5 : FROM scratch ---> Step 2/5 : WORKDIR $GOPATH/src/github.com/EDDYCJY/go-gin-example ---> Using cache ---> ee07e166a638Step 3/5 : COPY . $GOPATH/src/github.com/EDDYCJY/go-gin-example ---> 1489a0693d51Removing intermediate container e3e9efc0fe4dStep 4/5 : EXPOSE 8000 ---> Running in b5630de5544a ---> 6993e9f8c944Removing intermediate container b5630de5544aStep 5/5 : CMD ./go-gin-example ---> Running in eebc0d8628ae ---> 5310bebeb86aRemoving intermediate container eebc0d8628aeSuccessfully built 5310bebeb86a
Note that if your Golang application does not rely on any of the configuration files, it is possible to copy the executable file directly, and no other need to care
There are several solutions available here
- Dependency File Unified Management Mount
- Go-bindata, please.
...
So if you solve the problem of file dependencies , you don't need to put the directory COPY
in.
Four, the operation
$ docker run --link mysql:mysql -p 8000:8000 gin-blog-docker-scratch[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production. - using env: export GIN_MODE=release - using code: gin.SetMode(gin.ReleaseMode)[GIN-debug] GET /auth --> github.com/EDDYCJY/go-gin-example/routers/api.GetAuth (3 handlers)...
Run successfully, the program also receives requests normally
And then we'll look at the size, execute the docker ps -as
command.
$ docker ps -asCONTAINER ID IMAGE COMMAND ... SIZE9ebdba5a8445 gin-blog-docker-scratch "./go-gin-example" ... 0 B (virtual 132 MB)427ee79e6857 gin-blog-docker "./go-gin-example" ... 0 B (virtual 946 MB)
From the result, take the size Scratch
of a mirrored-based container to complete the goal
MySQL Mount data volume
Without any interference, the database is empty each time a container is started Mysql
. In addition, after the deletion of the container, the data is lost (there are all kinds of unexpected cases), very bad!
Data volumes
A data volume is designed to persist data, its lifecycle is independent of the container, Docker does not automatically delete the data volume after the container is deleted, and there is no mechanism for garbage collection to handle data volumes that do not have any container references. If you need to remove the data volume while removing the container. You can use this command when you delete a container. docker rm -v
A data volume is a special directory that can be used by one or more containers, bypassing UFS and providing many useful features:
- Data volumes can be shared and reused between containers
- Changes to the data volume will take effect immediately
- Updates to the data volume do not affect mirroring
- The data volume is always present by default, even if the container is deleted
Note: The use of the data volume, similar to Linux under the directory or file mount, the image is designated as a mount point in the directory of the files will be hidden, can be displayed to see the mounted data volumes.
How to Mount
First, create a directory to hold the data volume; sample directory /data/docker-mysql
, notice that --name
mysql
the container with the original name needs to be deleteddocker rm
$ docker run --name mysql -p 3306:3306 -e MYSQL_ROOT_PASSWORD=rootroot -v /data/docker-mysql:/var/lib/mysql -d mysql54611dbcd62eca33fb320f3f624c7941f15697d998f40b24ee535a1acf93ae72
Create success, check the directory /data/docker-mysql
, there are a lot of database files
Verify
Then you verify that the goal is to create some test tables and data, then delete the current container, re-create the container, and the database data will still exist (of course the data volume is pointing to the same)
I've verified it, what about you?
Reference
Sample code for this series
Books
- docker--from getting started to practicing