Jenkins+GitLab+Docker builds front-end automation build mirror container deployment (no local certificate, mapping certificate)

Foreword

Need to install the environment and knowledge points in advance:
1. Docker construction and basic operation
2. DockerFile file description
3. Jenkins construction and basic points

Purpose:
Package our front-end project into a mirror container and automatically publish and deploy it, which can be accessed by pull at any time

1. Manually deploy images and containers

1. Create a Dockerfile in the root directory of the current project and write the following code:

# The first stage: building the front-end output
FROM node:14.19.0 AS builder

WORKDIR /visualization
COPY . .
RUN npm install -g cnpm --registry=https://registry.npm.taobao.org
RUN cnpm install & amp; & amp; npm run build


# Second stage: generate the final container image
FROM nginx

COPY docker/nginx.conf /etc/nginx/conf.d/default.conf
COPY docker/docker-entrypoint.sh /docker-entrypoint.sh


WORKDIR /home/visualization
COPY --from=builder /visualization/dist .

RUN chmod +x /docker-entrypoint.sh

Detailed description of the code snippet:
Note: visualization is the name of the workspace, which can be replaced

Fragment Description
FROM node:14.19.0 AS builder Choose to use Node.js 14.19.0 as the base image and name the step “builder”
WORKDIR /visualization.COPY . . Set the working directory to “/visualization” and copy all files and directories in the current directory (root directory of the code warehouse) to the “/visualization” directory
1. RUN npm install -g cnpm –registry=https://registry.npm.taobao.org. 2. RUN cnpm install & amp; & amp; npm run build Install cnpm The package manager uses it to install project dependencies, and then executes the npm run build command to build, and the built front-end output is saved to the “/visualization/dist” directory
FROM nginx Choose to use Nginx as the base image, and name this step the default name “builder”
1, COPY docker/nginx. conf /etc/nginx/conf.d/default.conf 2. COPY docker/docker-entrypoint.sh /docker-entrypoint.sh Copy the Nginx configuration file and sh file under the docker folder to In the corresponding location
1, WORKDIR /home/visualization 2, COPY –from=builder /visualization/dist . Set the working directory to “/home/visualization” and copy the front-end artifacts from the “builder” stage into the current directory.
RUN chmod + x /docker-entrypoint.sh Execute the chmod + x command on “/docker-entrypoint.sh” to add executable permissions .

2. Create a new .dockerignore file in the root directory of the project and ignore the file

# Dependency directory
# https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git
node_modules
.DS_Store
dist
  
# node-waf configuration
.lock-wscript
  
# Compiled binary addons (http://nodejs.org/api/addons.html)
build/release
.dockerignore
Dockerfile
*docker-compose*
  
# Logs
logs
*.log
  
# Runtime data
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw*
pids
*.pid
*.seed
.git
.hg
.svn

3. Create a docker folder in the root directory of the current project

1. Create a new nginx.conf file to configure the front-end project to access the nginx configuration file
2. Create a new docker-entrypoint.sh file and execute the script to dynamically modify the proxy request address in nginx.conf

nginx.conf content
~Make modifications according to the project situation, if there is no gzip configuration front end, it can be deleted
~ /dev is the reference address of the front-end proxy across domains. It must be unified, and the address of the proxy to the back-end. The purpose of being a proxy is to dynamically change the proxy_pass address according to the container run.
~ If the project does not have https, you can delete the 443 listener
~If you have https, you need to configure the certificate ssl_certificate and ssl_certificate_key. The path of this file is later. When running the container (run) -v maps the directory of the host to the container, which is the directory of the container

server {<!-- -->
    listen 80;
    listen [::]:80;
    server_name localhost;
    
    # gzip config
    gzip on;
    gzip_min_length 1k;
    gzip_comp_level 9;
    gzip_types text/plain text/css text/javascript application/json application/javascript application/x-javascript application/xml;
    gzip_vary on;
    gzip_disable "MSIE [1-6]\.";
    #
    
    location / {<!-- -->
        root /home/visualization;
        index index.html index.htm;
    }
     
    location /dev {<!-- -->
        # Front-end/dev request proxy to back-end https://xxx.xxx/
        proxy_pass https://xxx.xxx/;
        
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_connect_timeout 1;
        proxy_buffering off;
        chunked_transfer_encoding off;
        proxy_cache off;
        proxy_send_timeout 30m;
        proxy_read_timeout 30m;
        client_max_body_size 500m;

        access_log /var/log/nginx/dev_access.log;
        error_log /var/log/nginx/dev_error.log;
    }
}

server {<!-- -->
    listen 443 ssl;
    listen [::]:443;
    # Configure domain name access items
    server_name xx.xx.xx.com;

    # gzip config
    gzip on;
    gzip_min_length 1k;
    gzip_comp_level 9;
    gzip_types text/plain text/css text/javascript application/json application/javascript application/x-javascript application/xml;
    gzip_vary on;
    gzip_disable "MSIE [1-6]\.";
    #
    
    # ssl certificate configuration start
    ssl_certificate /etc/nginx/cert/certbook_bundle.pem;
    ssl_certificate_key /etc/nginx/cert/certbook.key;
    ssl_session_timeout 5m;
    ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4;
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
    ssl_prefer_server_ciphers on;
    # End of ssl certificate configuration

    location / {<!-- -->
        root /home/visualization;
        index index.html index.htm;
    }

    location /dev {<!-- -->
        # Front-end/dev request proxy to back-end https://xxx.xxx/
        proxy_pass https://xxx.xxx/;
        
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_connect_timeout 1;
        proxy_buffering off;
        chunked_transfer_encoding off;
        proxy_cache off;
        proxy_send_timeout 30m;
        proxy_read_timeout 30m;
        client_max_body_size 500m;

        access_log /var/log/nginx/dev_access.log;
        error_log /var/log/nginx/dev_error.log;
    }
}

docker-entrypoint.sh content
Purpose of the file: Dynamically change the proxy address of the nginx configuration file, specifically to change a certain line
1. https://xxx.xxx/ is the backend interface address, the default address
2. sed -i ’22c ‘”$apiUrl”” /etc/nginx/conf.d/default.conf
For example: replace line 22 in the nginx.conf configuration file with a certain value
3. CERT means: https will be annotated when the variable exists, so as not to report an error if the https certificate cannot be found, which is mainly used when running locally

#!/usr/bin/env bash

API_BASE_PATH=$API_BASE_PATH;
if [ -z "$API_BASE_PATH" ]; then
    API_BASE_PATH="https://xxx.xxx/";
the fi

apiUrl="proxy_pass $API_BASE_PATH;"
sed -i '22c '"$apiUrl"'' /etc/nginx/conf.d/default.conf
sed -i '75c '"$apiUrl"'' /etc/nginx/conf.d/default.conf

# The variable CERT determines whether the certificate https is required, and $CERT does not need it if it exists
certOr="#"
if [ -n "$CERT" ]; then
 sed -i '45c '"$certOr"'' /etc/nginx/conf.d/default.conf
 sed -i '46c '"$certOr"'' /etc/nginx/conf.d/default.conf
 sed -i '60c '"$certOr"'' /etc/nginx/conf.d/default.conf
 sed -i '61c '"$certOr"'' /etc/nginx/conf.d/default.conf
the fi

nginx -g "daemon off;"

The overall file directory is as follows:

![Insert picture description here](https://img-blog.csdnimg. cn/88b97004e9cb4e86a6e96dfc67a937ee.png

4. Package the image and run the container

If there is no Docker environment locally, perform the following operations on the server:
1. Upload the project to the cloud server and create a project folder (the dist and node_modules folders of the project do not need to be uploaded)
2. On the remote server, cd to the project root directory
3. Build a Docker image

// hello refers to the image name
// 1.0.0 is the mirrored version
docker build . -t hello:1.0.0

4. Run the container
** -v /data/nginx/cert:/etc/nginx/cert refers to mapping the data/nginx/cert of the host to the etc/nginx/cert folder of the container, the above nginx.conf certificate path needs, if Ignore if https is not required
** -e “CERT=no” means that if there is no https certificate, the 443 monitoring in nginx.conf will be commented by default. If there is a certificate, configure the above -v. Must be added for local operation

//Method 1:
// contanier_hello is the container name
// -p 9090:80 maps port 80 in the container to port 8080 of the host. Port 80 is the configuration in nginx. Multiple ports and multiple configurations must be ensured that the server has opened this port
docker run -d --name contanier_hello -p 8080:80 -v /data/nginx/cert:/etc/nginx/cert hello:1.0.0

//Method 2:
// Change the nginx proxy address when running the container
// -e API_BASE_PATH is the variable defined in the sh file above. Change the address of the backend interface of nginx to http://www.baidu.com. This address must not be in the wrong format, otherwise nginx will not be able to parse it
docker run -d --name contanier_hello -p 8080:80 -v /data/nginx/cert:/etc/nginx/cert -e "API_BASE_PATH=http://www.baidu.com" hello:1.0.0

5. In this way, we can already run the access

6. [Extended] Publish the local image to the private server image repository
*** The prerequisite is to build the registry of Alibaba Cloud or Tencent Cloud
1. Enter the URL https://cr.console.aliyun.com/cn-hangzhou/repositories
2. Register an account
3. Create a namespace
4. Create a mirror warehouse
5. Select the newly created namespace and enter the warehouse information

docker login --username=xxxxx registry.cn-hangzhou.aliyuncs.com
docker push image name: version

2. Automatically build by Jenkins

This step is based on (1, 2, 3) in the first step, and then proceed as follows
Prerequisite: The server has already set up Jenkins
Special attention: When running the Jenkins container, you must map the Docker of the host machine, because you will use the dokcer command in the Jenkins container, otherwise you will not be able to find it. How to check whether you have mapped it? Enter the jenkins container and enter docker -v to see if there is version information. If not, you need to delete the container and run it again, for example:

docker run -d -uroot -p 10000:8080 -p 50000:50000 --name jenkins \
-v /home/jenkins_home:/var/jenkins_home \
-v /home:/home\
-v /etc/localtime:/etc/localtime \
-v /var/run/docker.sock:/var/run/docker.sock \
-v /usr/bin/docker:/usr/bin/docker\
jenkins/jenkins:lts

-v /var/run/docker.sock:/var/run/docker.sock
-v /usr/bin/docker:/usr/bin/docker
The above two sentences are to map Docker into the container

1. Create a Jenkins task


2. Configure General

3. Configure Shell variables

4. Configure the git source code (git at the project address)

5. Configure the build environment

6. Configure Shell to execute scripts

#!/bin/bash
docker -v

CONTAINER=${<!-- -->container_name}
PORT=${<!-- -->port}

# Check if the container with the same name exists and delete it
if docker ps -a --filter "name=$CONTAINER" --format '{<!-- -->{.ID}}' | xargs docker inspect --format='{<!- - -->{json .State.Running}}' | grep -qEe true; then
  docker stop $CONTAINER & amp; & amp; docker rm -f $CONTAINER
the fi

# Delete the previously built image
docker images | grep "${image_name}" | awk '{print $3}' | xargs docker rmi -f

# Build the image and add tags
docker build --no-cache -t ${<!-- -->image_name}:${<!-- -->tag} .

# Remove "depend on none image"
if docker images --filter 'dangling=true' -q --no-trunc; then
  docker rmi $(docker images --filter 'dangling=true' -q --no-trunc)
the fi

# run docker image
docker run -d --name $CONTAINER -p $PORT:80 -p 9091:443 -v /data/nginx/cert:/etc/nginx/cert --restart always ${<!-- -->image_name} :${<!-- -->tag}

echo '==================================================================================='
# The specific private server to log in to depends on which platform you created, Alibaba Cloud, Tencent Cloud, etc.
docker login --username=account --password=password registry.cn-hangzhou.aliyuncs.com
docker push ${<!-- -->image_name}:${<!-- -->tag}
echo '================== End push image =================='

7. Note: When running a container, the port must not be the same as the port mapped by other containers, it will be occupied

For example, -p 9091:443 above is to map port 443 of the container to port 9091 of the host, then the container in the host must not have port 9091. The only thing is that the server must open this port when re-mapping the port.

8. After the above configuration is completed, you can click Build, and it will automatically execute pulling code, project packaging, building mirror, running container, and publishing mirror

9. If others want to use your image, they can pull and run:

docker pull remote mirror name: [mirror version number]
Run the container:
// -e "CERT=no" No certificate needs to be added, it will hide 443 in the nginx configuration file

//method one:
// 1. Contanier_hello is the name of the container
// 2. -p 9090:80 maps port 80 in the container to port 8080 of the host. Port 80 is the configuration in nginx. Multiple configurations for multiple ports must ensure that the server has opened this port
docker run -d --name contanier_hello -p 8080:80 -e "CERT=no" hello:1.0.0

//Method 2:
// 1. Change the nginx proxy address when running the container. This method can be used when the backend wants to use the front-end code to run locally and request its own local ip
// 2. -e API_BASE_PATH is the variable defined in the sh file above. Change the address of the backend interface of nginx to http://www.baidu.com
// 3. API_BASE_PATH must be written correctly, do not write the format indiscriminately, nginx will not recognize it
docker run -d --name contanier_hello -p 8080:80 -e "API_BASE_PATH=http://www.baidu.com" -e "CERT=no" hello:1.0.0

Remarks:

1. This chapter is mainly based on the extension made in the previous article. The https certificate is not stored locally at the front end. When running the container, the certificate directory of the host is mapped to the inside of the container, and the certificate path inside the container can be introduced in nginx.conf
2. The CERT=no variable has been added. In order to solve the problem of nginx reporting an error due to no certificate to map when the local pull is performed, the 443 monitoring in nginx.conf will be commented when this variable is passed in.
3. There is another way to make https monitoring comments without passing in variables:
First delete all 443 monitors in nginx.conf, and delete the certOr code in docker-entrypoint.sh. When running the container (run), map the nginx file inside our container to a certain directory of the host, so that there are only 80 monitors, and no certificate error will be reported. Modify the nginx.conf file in the host directory, and then manually add 443 monitoring and configure certificates. When other people pull, they will not be troubled by the lack of certificates (because nginx in the mirror only has 80 listeners by default)

—– You’re done —–