Preparing a Dockerfile

To build a Docker image, we need to create a file called Dockerfile to specify the environment inside the Docker container. Our application will be securely isolated inside the container. If you're not familiar with Docker, the official documentation at https://docs.docker.com is a great place to start your exploration.

Before we start, we need to make sure that Docker is installed in our local environment so that we can test Dockerfile locally. In case you haven't installed it, you can download it from https://www.docker.com/get-started and follow the install instructions.

We will create a folder called docker in the root folder with the following structure:

├── docker
├── Dockerfile
├── application-docker.properties
├── env.list
└── start.sh

Let's go through these files one by one. The following is how the Dockerfile should look:

1. From openjdk:8-jre-alpine
2.
3. RUN apk add graphicsmagick=1.3.30-r0
4. RUN ln -s /usr/bin/gm /usr/local/bin/gm
5.
6. ADD app.jar /opt/taskagile/app.jar
7. ADD application-docker.properties /config/application-
docker.properties
8.
9. EXPOSE 8080 9000
10.
11. ENTRYPOINT ["java", "-jar", "/opt/taskagile/app.jar"]

Every Dockerfile begins with the From instruction, which tells Docker to initialize a new build stage and, in our case, use openjdk:8-jre-alpine as the base image. This OpenJDK image comes from Docker's public repository. In line 3, with RUN instruction, we ask Docker to install the GraphicMagick dependency. In line 4, we create a soft link from /usr/bin/gm to /usr/local/bin/gm, which is used for creating image thumbnails in our application. In line 6, we add the docker/app.jar file to the Docker image. The docker/app.jar file will be copied from the target folder into the docker folder by command defined in Jenkinsfile, as you will see in the next section. In line 7, we add the application-docker.properties file to the /config folder inside the Docker image. After that, we use an EXPOSE instruction to inform Docker that the container will listen to the 8080 and 9000 ports at runtime. The EXPOSE instruction does not actually publish the port. It is more like a document between the builder of the Docker and the runner of the container, about which ports are intended to be published. In line 11, the ENTRYPOINT instruction allows us to run the Docker container as an executable, and the java -jar /opt/taskagile/app.jar command will be executed when the container starts.

As you might remember, in the last chapter, when we prepared for the rolling-out phase, we mentioned that we will put the .properties file in /opt/taskagile/config/, so that when we execute the service taskagile start command, Spring Boot will pick up the properties defined in that file. When we run with Docker, we no longer need to install the application as a service. We will use the java -jar command to start it and, by default, Docker will run this command from the root path inside the container. That's why we put the application-docker.properties inside /config folder.

application-docker.properties is the properties file that we use to provide the ability for Docker to override settings through environment variables. Here is how it looks:

spring.datasource.url=jdbc:mysql://${TASK_AGILE_DB_HOST}:3306/${TASK_AGILE_DB_NAME}?useSSL=false
spring.datasource.username=${TASK_AGILE_DB_USERNAME}
spring.datasource.password=${TASK_AGILE_DB_PASSWORD}

spring.rabbitmq.host=${TASK_AGILE_MQ_HOST}
spring.rabbitmq.port=${TASK_AGILE_MQ_PORT}
spring.rabbitmq.username=${TASK_AGILE_MQ_USERNAME}
spring.rabbitmq.password=${TASK_AGILE_MQ_PASSWORD}

spring.mail.host=${TASK_AGILE_MAIL_HOST}
spring.mail.port=${TASK_AGILE_MAIL_PORT}

As you can see, in this properties file, we use environment variables for external resources that the application depends on. These environment variables will be defined in Docker's env.list environment file and passed to the container via the --env-file argument of the docker run command. Here is how it looks:

TASK_AGILE_TOKEN_SECRET_KEY=60dKuW2Qpc3YkUoaa9i6qY5cyaGgQM8clfxpDGWS3sY=

TASK_AGILE_DB_HOST=<Your DB host>
TASK_AGILE_DB_NAME=task_agile
TASK_AGILE_DB_USERNAME=<Your DB username>
TASK_AGILE_DB_PASSWORD=<Your DB password>

TASK_AGILE_MQ_HOST=<Your MQ host>
TASK_AGILE_MQ_PORT=5672
TASK_AGILE_MQ_USERNAME=<Your MQ username>
TASK_AGILE_MQ_PASSWORD=<Your MQ password>

TASK_AGILE_MAIL_HOST=<Your Mail host>
TASK_AGILE_MAIL_PORT=25

TASK_AGILE_CDN_URL=<Your CDN URL>

In our production environment, https://taskagile.com, given the fact that it will be just a demo, we will host everything inside a single EC2 instance, including the database, the messaging queue, and the Docker container of our application. Both the database and the message queue will be installed in the same EC2 instance directly. In reality, a more practical way is either to put them both inside separate containers or use corresponding cloud services, like AWS's RDS and AWS's MQ. Here, we simply put them in the same box to save a few bucks.

If you're using Docker for Mac locally, you can also use the special host.docker.internal DNS name as the value of TASK_AGILE_DB_HOST, TASK_AGILE_MQ_HOST, and TASK_AGILE_MAIL_HOSTOn Linux, you can use the host's private IP for these hosts. Using 127.0.0.1 or localhost won't work.

In practice, the env.list file in the docker folder is used as a template. We will save the actual env.list file in S3 and download it during deployment.

You can use the following command to test Docker in your local environment:

$ mvn clean package
$ cp target/app-0.0.1-SNAPSHOT.jar docker/app.jar
$ docker build -t taskagile:latest docker/
$ docker run --rm --name taskagile -e "SPRING_PROFILES_ACTIVE=dev"
-p 8080:8080 -p 9000:9000 taskagile
If you experience a database connection refused issue when the connection from the inside of the container to the MySQL database in the host, then you might want to create a MySQL user that has been granted to access from addresses other than localhost. 

The following is how start.sh looks:

docker pull taskagile/vuejs.spring-boot.mysql:$1
docker container stop taskagile
docker run --detach --rm --name taskagile --env-file ./env.list
-e "SPRING_PROFILES_ACTIVE=staging,docker"
-p 8080:8080 -p 9000:9000
taskagile/vuejs.spring-boot.mysql:$1

The start.sh shell script serves as a bootstrap of the application. First, it pulls the Docker image of a specific tag from Docker's repository. $1 is a variable used to get the first argument passed to the script. Once the Docker image is pulled, the script stops the current Docker container and then starts a new one. Let's go through the options in this command in detail:

  • With the --detach option, the container will run in the background.
  • With the --rm option, Docker will automatically remove the "taskagile" container when it exists.
  • With the --name option, the started new container will be named "taskagile".
  • With the --env-file option, Docker will read the environment variables from the env.list file.
  • With the -e option, Docker will set the SPRING_PROFILES_ACTIVE environment variable with the staging,docker value. Make sure to put "docker" as the last profile, so that settings in env.list can be applied.
  • With the -p option, Docker will bind port 8080 of the container to port 8080 of the host machine, as well as port 9090.

The env.list and start.sh files will be copied over to the server during the deployment, as we will see in the next section.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.191.195.111