Due to the word limit, can only be developed, connected to the above juejin.cn/post/690853…

Deployment

Docker uses static HTML files and nignx images to create and run a container, but it is much more than that. Let’s deploy this blog as an example to explore some of the knowledge inside.

Source code address: github.com/Moon-Future…

In order to maintain docker files uniformly, docker-related files will be placed under their respective directories under docker files below, so it is necessary to determine the context of topic construction.

1. Deploy the foreground blog

Port mapping 9000:9000. Server port: container port. If an online server is deployed, configure the corresponding port number in the security group

Create docker directory in blog directory, docker directory create three files

  • .dockerignore: Ignore list of copied files
  • Dockefile
  • docker-compose.yml

.dockerignore

node_modules
.next
Copy the code

Dockefile

# node mirror
The # apline version of Node will be much smaller
FROM node:12-alpine

Create a directory in the container
RUN mkdir -p /usr/src/app

The following commands will be executed in the current directory
WORKDIR /usr/src/app

# copy package. Json
COPY package.json /usr/src/app

# install dependencies
RUN npm i --production --registry=https://registry.npm.taobao.org

# copy all other files to container (except directories and files in.dockerignore)
COPY . /usr/src/app

# build
RUN npm run build

Expose port 9000
EXPOSE 9000

Dokcerfile can only have one CMD command per file. If multiple, only the last one will be executed
CMD [ "npm"."start" ]
Copy the code

Docker images are layered, and the following points are important:

  • Dockerfile in the mirror every instruction to create a new layer, each RUN is an instruction docs.docker.com/engine/refe…
  • The image layer will be cached and reused
  • When the Dockerfile directive is changed, the copied file is changed, or the variables specified when the image is built are different, the corresponding mirror layer cache is invalidated
  • When a layer’s image cache is invalidated, all subsequent layer caches are invalidated
  • The image layer is immutable, so if we add a file to one layer and then delete it from the next, the image will still contain the file (but the file is not visible in the Docker container).

So we first copy package.json, then RUN NPM I to install dependencies and form a mirror layer, then copy all other files to form a mirror layer, then if the code has changed but package.json has not changed, when executing again, no dependencies will be installed. It saves a lot of time. If package.json changes, RUN RUN I will be re-executed to install dependencies.

If the image generated, at this time to delete imageA, re-generate, remember Sir Into a new image imageB, so it will reuse the NPM package, if the first delete imageA, and then the new generation of imageB, it will reinstall the dependency.

To generate the image, run the following command in the blog directory: react_blog

$ docker build -f docker/Dockerfile . -t react_blog:blog
Copy the code

The first time you run the install dependency is a bit slow. When I first started using Node-sass, the installation was always wrong, but I switched to less. If you want to install yarn, the NPM related commands in Dockerfile can also be replaced with the appropriate YARN commands.

After a long wait for the build to succeed, the following information is the file generated by NPM run Build

Look at the resulting image

$ docker imagesREPOSITORY TAG IMAGE ID CREATED SIZE react_blog blog fef06dfed97f 3 minutes ago 329MB nginx latest ae2feff98a0c 31 hours Ago 133MB Node 12- Alpine 844F8bb6a3f8 3 weeks ago 89.7MBCopy the code

Then build and run the container

$ docker run -itd -p 9000:9000 --name react_blog_blog react_blog:blog
Copy the code

Here the parameters are explained again:

  • -iThe argument keeps the container’s standard input on, –interactive
  • -tThe argument lets Docker assign a dummy terminal and bind it to the container’s standard input, –tty
  • -d–detach (Run container in background and print container ID)
  • --nameParameter Specifies a unique container name, or a random container name if not specified

– it generally and at the same time, the -d parameter, that is, if not add container operation success, will enter a terminal command interface, if you want to quit, Ctrl + C, only after exit containers also dropped out, docker ps – a can see container status is Exited (0). You can start it again using docker Start Container. Add -d and the container will run directly in the background, normally add -d. You can try it, and then delete the container.

If the above container is successfully executed, the page can be accessed from the browser using server IP :9000, or localhost:9000 for Mac or Windows.

docker-compose.yml

version: '3'
services:
  web:
    build:
      context: ../
      dockerfile: ./docker/Dockerfile
    image: react_blog:blog
    ports:
      - 9000: 9000
    container_name: react_blog_blog
Copy the code

Docker build, docker run and other commands are used to create containers, regenerate and run containers. The commands are not easy to remember and input. Here we can use docker-compose to simplify the execution of the command.

Let’s take a look at the document:

  • Web: indicates the service name
  • Build: Build related, to be executed laterdocker-composeThe command path must be the same as docker-comemage. yml, so the context construction context selects the source directory of the previous layer, and dockerfile is the dockerfile in the current directory
  • Image: the name of the image. If there is an image, use it directly. If there is no image, use Dockerfile to generate it
  • Ports: port mapping
  • Container_name: specifies the name of the container, unique. If no, it isCurrent directory _ Service name _indexIndex number (summation from 1), if here isdocker_web_1

Docker rm -f react_blog_blog: compose up docker rm -f react_blog_blog: compose up docker rm -f react_blog_blog: compose up docker rm -f react_blog_blog: compose up docker-compose

Execute the command in the docker directory

$ docker-compose up -d
Copy the code

Docker-compose up -d –build can be used to regenerate the image

The above blog front-end page deployment, now just separate deployment learning, will be deleted and backend and interface deployment together.

2. Deploy the background admin

Port mapping 9001:9001: Server port: container port. If an online server is deployed, configure the corresponding port number in the security group

Now let’s deploy Admin separately. In Docker, we have already used Admin to simply deploy learning to make images and build containers. Here, we still first generate generate environment static files under the admin directory

$ npm run build
Copy the code

Create a docker directory under admin to store docker-related files. Create the following files under the docker directory:

Dockerfile

FROM nginx

# delete default configuration of Nginx
RUN rm /etc/nginx/conf.d/default.conf

EXPOSE 80
Copy the code

Notice some differences between here and above,

  • The default configuration of nginx is removed here, and we will configure one ourselves
  • There is no COPY of static files to the container in docker-comemage. yml by hanging them

docker-compose.yml

version: '3'
services:
  admin:
    build: 
      context: ../
      dockerfile: ./docker/Dockerfile
    image: react_blog:admin
    ports: 
      - 9001: 80
    volumes: 
      - ../build:/www
      - ./nginx.conf:/etc/nginx/conf.d/nginx.conf
    container_name: react_blog_admin
Copy the code

Volumes is an array of volumes that correspond to the host file in the container

  • . /build:/ WWW, the files in build are hung in the container/WWW directory
  • . / nginx. Conf: / etc/nginx/conf. D/nginx. Conf, nginx. Conf mounted to the container/etc/nginx/conf. D/nginx. Conf this file

The advantage of this is that when the files on the host are changed, the files in the container are automatically changed, and accordingly the files in the container are changed as well. In this way, after the source code changes and the build is repackaged, you only need to put it in the corresponding directory of the server, and the class capacity under the container class/WWW will be the latest, instead of executing the Dockerfile again and again to copy the build file into the container, and the database data is usually stored in the host. To prevent loss of data when the container is deleted.

The same applies to the nginx.conf configuration file, but after the nginx configuration file is changed, the following container needs to be restarted to take effect

To run the container, run the command in the docker directory

$ docker-compose up -d
Copy the code

Check whether the container runs successfully

$ docker ps -aCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 7DB8CE1C6814 react_blog:admin "/ docker-entryPoint...." 16 minutes ago Up 16 minutes 0.0.0.0:9001->80/ TCP react_blog_adminCopy the code

Run docker logs container to view logs that fail

If successful, you can access the page from the browser using server IP :9001, Mac or Windows local localhost:9001.

nginx.conf

server {
  listen 80;
  sendfile on;
  sendfile_max_chunk 1M;
  tcp_nopush on;
  gzip_static on;

  location / {
    root /www;
    indexindex.html; }}Copy the code

Root remembers to hang in the same directory as above

3. Deploy the service interface service + Mysql

Port mapping 9002:9002: Server port: container port. If an online server is deployed, configure the corresponding port number in the security group

Create a docker directory under the service directory. Create the following files under the docker directory:

.dockerignore

node_modules
.github
article
Copy the code

The Article directory is used to house blog content files

Dockerfile

FROM node:alpine

Configure environment variables
ENV NODE_ENV production

This is the directory of the files in the container
RUN mkdir -p /usr/src/app 

Set the working directory
WORKDIR /usr/src/app

# copy package.json file to working directory
#!!!!! Important: Package. json needs to be added separately.
# Docker builds the image layer by layer. Only when this layer changes, the corresponding layer will be rebuilt.
# If package.json is added to the image with the source code, the NPM module will need to be reinstalled every time the source code is modified, which is not necessary.
# So, the correct order is: add package.json; Install the NPM module. Add source code.
COPY package.json /usr/src/app/package.json

# Install NPM dependencies (using Taobao mirror source)
# If using an overseas server, there is no need to use taobao mirror source, that is, 'RUN NPM I'.
RUN npm i --production --registry=https://registry.npm.taobao.org

Copy all source code to the working folder
COPY . /usr/src/app

Expose the container port
EXPOSE 9002

CMD npm start
Copy the code

docker-compose.yml

version: '3'
services:
  service:
    build:
      context: ../
      dockerfile: ./docker/Dockerfile
    image: react_blog:service
    ports:
      - 9002: 9002
    depends_on:
      - db
    environment:
      MYSQL_HOST: localhost
      MYSQL_USER: root
      MYSQL_PASSWORD: 8023
    volumes:
      - ../article:/usr/src/app/article
    container_name: react_blog_service
  db:
    image: mysql
    # volumes:
    # - /db_data:/var/lib/mysql
    ports:
      - 33061: 3306
    command: --default-authentication-plugin=mysql_native_password
    environment:
      MYSQL_ROOT_PASSWORD: 8023
      # MYSQL_USER: root
      MYSQL_PASSWORD: 8023
      MYSQL_DATABASE: react_blog
    container_name: react_blog_mysql
Copy the code

Note that there are two services running: service and db

Service The service is the back-end interface:

  • Deponds_on: Run the services in the Deponds_on list first, in case the dependency is not yet run and it will report an error

  • Command: Starting with MySQL8.0, caching_sha2_password is used as the default encryption rule. This command can be used to change the encryption rule. Client does not support authentication protocol requested by server; consider upgrading MySQL client

  • /config/secret.js(secret-temp.js); /config/secret.js

    /** * secret.js template */
    
    module.exports = {
      // Mysql connection configuration
      mysql: {
        host: process.env.MYSQL_HOST || 'localhost'.port: process.env.MYSQL_PORT || '3306'.user: process.env.MYSQL_USER || 'xxx'.password: process.env.MYSQL_PASSWORD || 'xxx'.database: process.env.MYSQL_DATABASE || 'xxxxxx',},// jwt
      tokenConfig: {
        privateKey: 'xxxxxxxxxxxxxxxxxxxxxxxxx',}}Copy the code
  • Volumes: I’ve already written the post to the host and mounted it to the container

The db service is Mysql database:

  • Volumes: Data Settings are stored on the host

  • Ports: Port mapping, which allows the host to access Mysql inside the container through port 33061. We can then connect to it through Navicat or other database visualization tools

  • Environment: Configures the database

    • MYSQL_ROOT_PASSWORD must be used to set the password of user ROOT
    • MYSQL_USER MYSQL_USER;Pay attention toIf root is used, an error is reportedERROR 1396 (HY000): Operation CREATE USER failed for 'root'@'%' , according to theGithub.com/docker-libr…You can log in as user root by default. If you have a root account, you will create a new one
    • MYSQL_PASSWORD Specifies the password used by the container to log in to the Mysql database. If the user name is MYSQL_USER, the password is MYSQL_ROOT_PASSWORD. If the user name is ROOT, the new password is set
    • MYSQL_DATABASE create a react_blog database, you can also create a container or Navicat database, but you need to connect to the react_blog database. (After the database is created, run both containers.)

Execute the command in the service/docker directory

$ docker-compose up -d
Copy the code

If it works, look at images and Containers

$ docker imagesREPOSITORY TAG IMAGE ID CREATED SIZE react_blog service 89139d833458 About an hour ago 150MB react_blog admin 1b5d6946f1fe 32 hours ago 133MB react_blog blog fef06dfed97f 35 hours ago 329MB nginx latest ae2feff98a0c 2 days ago 133MB mysql latest AB2F358b8612 6 days ago 545MB node 12- Alpine 844F8bb6a3F8 3 weeks ago 89.7MBCopy the code

You can see more Mysql and react_blog:blog mirrors

$ docker ps -aCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 5878940d7626 react_blog:blog "docker..." 5 seconds ago Up 4 seconds 0.0.0.0:9000->9000/ TCP react_blog_blog 3bff0060de19 react_blog:admin "/docker..." 3 minutes ago Up 18 seconds 0.0.0.0:9001->80/ TCP react_blog_admin d8a899232e8c react_blog:service "docker..." About a Exited (1) 5 minutes ago react_blog_service a9DA07ff5cae mysql "docker..." About an hr 33060/ TCP, 0.0.0.0:33061->3306/ TCP react_blog_mysqlCopy the code

React_blog_service and react_blog_mysql failed to run. The react_blog_service failed to run

$ docker logs react_blog_service. Errno: "ECONNREFUSED" code: "ECONNREFUSED" syscall: "connect" address: "127.0.0.1" Port: 3306 fatal: true name: "ECONNREFUSEDError" pid: 47 hostname: d8a899232e8c ...Copy the code

Docker-comemage. yml: MYSQL_HOST=localhost; docker-comemage. yml: MYSQL_HOST=localhost; Localhost is the react_blog_service’s own IP (127.0.0.1), but it cannot access react_blog_mysql. We will address this problem in the next section, starting with Mysql.

The Mysql container is running successfully. We can access the container and connect to Mysql. Remember how to access the container

$ docker exec -it react_blog_mysql /bin/sh

$ ls
bin  boot  dev	docker-entrypoint-initdb.d  entrypoint.sh  etc	home  lib  lib64  media  mnt  opt  proc  root  run  sbin  srv  sys  tmp  usr  var
$ mysql -uroot -pEnter password: Welcome to the MySQL monitor. Commands end with ; or \g. Your MySQL connection id is 12 Server version: 8.0.22 MySQL Community Server - GPL Copyright (c) 2000, 2020, Oracle and/or its affiliates. All rights reserved. Oracle is a registered trademark of Oracle Corporation and/or its affiliates. Other names may be trademarks of their respective owners. Type 'help; ' or '\h' for help. Type '\c' to clear the current input statement.
mysql> 

Copy the code

You can see that Mysql is connected successfully. Type exit to exit the container. We can also use visual tools to connect, in this case Navicat

Mysql > port 33061; port 33061; port 33061; port 33061;

Lost connection to Mysql server at ‘reading initial communication packet’ lost connection to Mysql server at ‘reading initial communication packet’ The solution is to run the systemctl start mysqld.service command to start the Mysql service. I don’t know where the Mysql service is affected, but LATER I will directly connect to the host Mysql without using containers, so that I can manage data in a unified way with other projects. My task is more convenient, and the data is safer.

4. Connect containers

The service failed to connect to the database. Refer to Docker Dream Builder series (I) : container interconnection

4.1 the Network type

A Network, as the name implies, enables different containers to communicate with each other. First of all, it is necessary to list the five drivers of Docker Network:

  • bridge: The default driver mode, namely “bridge”, is usually usedstand-alone(To be more precise, a single Docker daemon)
  • overlay: Overlay network is used to connect multiple Docker daemonsThe clusterDocker Swarm will be highlighted in future articles
  • host: Directly use the host (that is, the machine running Docker) network, only for Docker 17.06+ cluster service
  • macvlan: Macvlan Networks Allow each container to be displayed as a physical device by assigning a MAC address to it, suitable for applications (such as embedded systems, Internet of Things, and so on) that want to connect directly to a physical network
  • none: Disables all networks for this container

By default, containers are created under the Bridge network, as shown below. Each container can connect to the HOST through dokcer0 and is assigned an IP address. In this case, containers need to enter each other’s IP address to connect to each other.

Viewing the Network List

$ docker network lsNETWORK ID NAME DRIVER SCOPE a75e040b03ed bridge bridge local 13545e6a3970 docker_default bridge local 5ec462838a1c host  host local c726e6887f10 none null localCopy the code

There are 4 networks here, the default is only 3, there is no docker_default, I also write here to find that created a docker_default network, search the official website (Networking in Compose) found. Docker-compose: docker-compose: docker-compose: docker-compose: docker-compose: docker-compose: docker-compose: docker-compose: docker-compose: docker-compose: docker-compose: docker-compose: docker-compose Docker-comemage. yml is stored in the docker directory, so several containers are created in the docker_default network. You can view network details by following the command

$ docker network inspect docker_default[ { "Name": "docker_default", "Id": "13545e6a39708344b363b7fc16eefeb6775c37773222804ebd5b5fb6f28c38bb", "Created": 2020-12-16T11:03:37.2152073+08:00", "Scope": "local", "Driver": "bridge", "EnableIPv6": false, "IPAM": { "Default", "Options", null, "Config" : [{" Subnet configures ":" 172.24.0.0/16 ", "Gateway" : "172.24.0.1}]}," Internal ": false, "Attachable": true, "Ingress": false, "ConfigFrom": { "Network": "" }, "ConfigOnly": false, "Containers": { "23891d43187e046eea25936dc0ab703964cc6c7213bb150ae9529da3e2e57662": { "Name": "react_blog_mysql", "EndpointID": "649857f928e0444500cfd296035869678bf26162d429a4499b262776b2a1d264", "MacAddress": "02:42:ac:18:00:03", "IPv4Address": "" IPv6Address 172.24.0.3/16", ":" "}, "3 bff0060de19fc973039c07c1931e2c1efe30c6707bcd77d2ff7ea4dc01aaf63" : {" Name ": "react_blog_admin", "EndpointID": "25d8fa518b0ce27498f562372c3424aee174cb1d8fbf9f2445f1c6af8e6aab7f", "MacAddress": "02:42: ac: 18:00:02", "IPv4Address" : "172.24.0.2/16", "IPv6Address" : "" }, "5878940d7626a9fb20622cde4002075e390e5036036bafb99d80454d6cba594b": { "Name": "react_blog_blog", "EndpointID": "a3f8ee36eda09f524be7ea16a67a1e13e62cf558e5480218bb523f877d478e4a", "MacAddress": "02:42:ac:18:00:04", "IPv4Address": "" IPv6Address 172.24.0.4/16", ":" "}}, "Options" : {}, "Labels" : {" com.docker.compose.net work ": "Default", "com.docker.com pose. Project" : "docker", "com.docker.com pose. Version" : "1.25.1"}}]Copy the code

You can see that the docker_default gateway address is 172.24.0.1, and the other container IP addresses are 172.24.0.3, 172.24.0.2, and 172.24.0.4 respectively, so the situation here is like this

The default network bridge can only be connected to the container by entering the IP address, and the custom network can also be connected by the container name

On user-defined networks like alpine-net, containers can not only communicate by IP address, but can also resolve a container name to an IP address. This capability is called automatic service discovery.

Networking with standalone containers

This avoids the problem of the container IP changing each time it is generated. /docker/ docker-comemage. yml

version: '3'
services:
  service:
    build:
      context: ../
      dockerfile: ./docker/Dockerfile
    image: react_blog:service
    ports:
      - 9002: 9002
    restart: on-failure
    depends_on:
      - db
    environment:
      MYSQL_HOST: react_blog_mysql # change localhost to mysql container name, in the same custom network, will automatically resolve to IP connection
      MYSQL_USER: root
      MYSQL_PASSWORD: 8023
    volumes:
      - ./article:/usr/src/app/article
    container_name: react_blog_service
  db:
    image: mysql
    ports:
      - 33061: 3306
    restart: on-failure
    command: --default-authentication-plugin=mysql_native_password
    environment:
      MYSQL_ROOT_PASSWORD: 8023
      MYSQL_PASSWORD: 8023
      MYSQL_DATABASE: react_blog
    container_name: react_blog_mysql
Copy the code

Run the command here

$ docker-compose up -d --build
Copy the code

Docker logs react_blog_service does not return an error, indicating that the database has been connected. We have added a GET test interface in the code. In the browser input IP: 9002 / API/test/get or localhost: 9002 / API/test/get, returns a json object

{"message":"Hello You Got It"}
Copy the code

I’ve been trying this for a long time, I’ve been having problems,

  • ERROR 1396 (HY000): MYSQL_USER: root; Operation CREATE USER failed for ‘root’@’%’, according to github.com/docker-libr… You can log in as user root by default. If you have a root account, you will create a new one. Drop MYSQL_USER and use root to log in

  • Service docker start react_blog_service restart service docker start react_blog_service restart service docker start react_blog_service If you want to restart the service, you will not need to restart it. If you want to restart the service, you will not need to restart it. If you want to restart the service, you will not need to restart it.

    Depends_on has been used. I am not sure why this issue exists, but the following is an example:

    version: "3.9"
    services:
      web:
        build: .
        depends_on:
          - db
          - redis
      redis:
        image: redis
      db:
        image: postgres
    Copy the code

    Depends_on does not wait for DB and Redis to be “ready” before starting web-only until they have been started. If you need to wait for a service to be ready, see Controlling startup order for more on this problem and strategies for solving it.

    Depends_on does not wait for DB and Redis to be “ready” before starting Web – only before they start.

    That should be the only reason

Let’s look at the docker_default network

$ docker network inspect docker_default[ { "Name": "docker_default", "Id": "13545e6a39708344b363b7fc16eefeb6775c37773222804ebd5b5fb6f28c38bb", "Created": 2020-12-16T11:03:37.2152073+08:00", "Scope": "local", "Driver": "bridge", "EnableIPv6": false, "IPAM": { "Default", "Options", null, "Config" : [{" Subnet configures ":" 172.24.0.0/16 ", "Gateway" : "172.24.0.1}]}," Internal ": false, "Attachable": true, "Ingress": false, "ConfigFrom": { "Network": "" }, "ConfigOnly": false, "Containers": { "3bff0060de19fc973039c07c1931e2c1efe30c6707bcd77d2ff7ea4dc01aaf63": { "Name": "react_blog_admin", "EndpointID": "25d8fa518b0ce27498f562372c3424aee174cb1d8fbf9f2445f1c6af8e6aab7f", "MacAddress": "02:42:ac:18:00:02", "IPv4Address": "" IPv6Address 172.24.0.2/16", ":" "}, "5878940 d7626a9fb20622cde4002075e390e5036036bafb99d80454d6cba594b" : {" Name ": "react_blog_blog", "EndpointID": "a3f8ee36eda09f524be7ea16a67a1e13e62cf558e5480218bb523f877d478e4a", "MacAddress": "02:42: ac: 18:00:04", "IPv4Address" : "172.24.0.4/16", "IPv6Address" : "" }, "83005eec8d50071a6c23a2be4af8552983c09c532e937f04d79f02f8eb68acc9": { "Name": "react_blog_mysql", "EndpointID": "265ed7793c98287a05ccf8997e81671287a02ee8ea464984996083a34abe10dd", "MacAddress": "02:42:ac:18:00:03", "IPv4Address": "" IPv6Address 172.24.0.3/16", ":" "}, "937339 a37ce726e704ec21b31b4028a97967a00de01438557e5a60d8538a51c8" : {" Name ": "react_blog_service", "EndpointID": "934d26f32a2b23e2cb4691020cb93d26c97b9647108047b492c3f7dd2be6faef", "MacAddress": "02:42: ac: 18:00:05", "IPv4Address" : "172.24.0.5/16", "IPv6Address" : ""}}," Options ": {}," Labels ": { "com.docker.compose.network": "default", "com.docker.compose.project": "docker", "com.docker.compose.version": "1.25.1"}}]Copy the code

React_blog_service has been added to the network. The IP address is 172.24.0.5

4.2 Customizing networks

Docker-comemess. yml files are stored in the docker directory, so they are all in the same network. If the name is changed, it will not be in the same network, and the project may also have a docker directory later. It’s not good to be all in one network, so here we are from the network that defines this project.

blog/docker/docker-compose.yml

version: '3'
services:
  blog:
    build:
      context: ../
      dockerfile: ./docker/Dockerfile
    image: react_blog:blog
    ports:
      - 9000: 9000
    networks: 
      - react_blog
    container_name: react_blog_blog
networks: 
  react_blog:
Copy the code

admin/docker/docker-compose.yml

version: '3'
services:
  admin:
    build:
      context: ../
      dockerfile: ./docker/Dockerfile
    image: react_blog:admin
    ports:
      - 9001: 80
    volumes:
      - ../build:/www
      - ./nginx.conf:/etc/nginx/conf.d/nginx.conf
    networks:
      - react_blog
    container_name: react_blog_admin
networks:
  react_blog:
Copy the code

service/docker/docker-compose.yml

version: '3'
services:
  service:
    build:
      context: ../
      dockerfile: ./docker/Dockerfile
    image: react_blog:service
    ports:
      - 9002: 9002
    depends_on:
      - db
    environment:
      - MYSQL_HOST=react_blog_mysql # change localhost to mysql container name, in the same custom network, will automatically resolve to IP connection
      - MYSQL_USER=root
      - MYSQL_PASSWORD=8023
    volumes:
      - ./article:/usr/src/app/article
    networks:
      - react_blog
    container_name: react_blog_service
  db:
    image: mysql
    ports:
      - 33061: 3306
    command: --default-authentication-plugin=mysql_native_password
    environment:
      - MYSQL_ROOT_PASSWORD=8023
      - MYSQL_USER=root
      - MYSQL_PASSWORD=8023
      - MYSQL_DATABASE=react_blog
    networks:
      - react_blog
    container_name: react_blog_mysql
networks:
  react_blog:
Copy the code
  • Networks of the same class as services: create a new network and add the directory name docker_react_blog to the network’s final name.
  • React_blog (react_blog) react_blog (react_blog) react_blog (react_blog

Note:

In this way, all networks generated in docker-comemage. yml will be added with the current directory name. If you do not want to add the current directory name, you can create your own network

$ docker network create my_net
Copy the code

And then in docker-comemage.yml

version: '3'
services:
  service:
    build:
      context: ../
      dockerfile: ./docker/Dockerfile
    image: react_blog:service
    ports:
      - 9002: 9002
    depends_on:
      - db
    environment:
      - MYSQL_HOST=react_blog_mysql # change localhost to mysql container name, in the same custom network, will automatically resolve to IP connection
      - MYSQL_USER=root
      - MYSQL_PASSWORD=8023
    volumes:
      - ./article:/usr/src/app/article
    networks:
      - my_net
    container_name: react_blog_service
  db:
    image: mysql
    ports:
      - 33061: 3306
    command: --default-authentication-plugin=mysql_native_password
    environment:
      - MYSQL_ROOT_PASSWORD=8023
      - MYSQL_USER=root
      - MYSQL_PASSWORD=8023
      - MYSQL_DATABASE=react_blog
    networks:
      - my_net
    container_name: react_blog_mysql
networks:
  my_net:
  	external: true
Copy the code

External will use the network already created (my_net) without creating or adding the directory name.

Let’s recreate the container, first deleting all containers

$ docker stop $(docker ps -aq) 
$ docker rm $(docker ps -aq)
Copy the code

Docker-compose up -d: Creating network “docker_react_blog” with the default driver: docker-compose up -d: Creating network “docker_react_blog” with the default driver A new network has been created

$ docker network ls
NETWORK ID     NAME                DRIVER    SCOPE
a75e040b03ed   bridge              bridge    local
13545e6a3970   docker_default      bridge    local
e1ceb437a4fd   docker_react_blog   bridge    local
5ec462838a1c   host                host      local
c726e6887f10   none                null      local

$ docker network inspect docker_react_blog[ { "Name": "docker_react_blog", "Id": "e1ceb437a4fdc5de91e51ff8831e21b565c92754159ad7057de36758e548a92f", "Created": "2020-12-19T01:39:02.201644444+08:00", "Scope": "local", "Driver": "bridge", "EnableIPv6": false, "IPAM": { "Default", "Options", null, "Config" : [{" Subnet configures ":" 172.18.0.0/16 ", "Gateway" : "172.18.0.1}]}," Internal ": false, "Attachable": true, "Ingress": false, "ConfigFrom": { "Network": "" }, "ConfigOnly": false, "Containers": { "00da404f6f050b9b2f20e39bbb136fef614e8dfee85ec31bd6000bfd59cc2dab": { "Name": "react_blog_mysql", "EndpointID": "1cb966cc731eca3e9721e6d3edcfcac6152b66051faa934557f567e9e36c75c6", "MacAddress": "02:42:ac:12:00:04", "IPv4Address": "" IPv6Address 172.18.0.4/16", ":" "}, "ad1480e48e8e7ed160b1d4bcf7eed77d74505aea7581d48d8931206772b5d805" : {" Name ": "react_blog_service", "EndpointID": "8866c3457382d6baa945da09aef40da54c7dfdea0f393485001c35bb37d201a0", "MacAddress": "02:42: ac: 12:00:05", "IPv4Address" : "172.18.0.5/16", "IPv6Address" : "" }, "b518d40b5021d3fdec7b7e62fbaa47b8a705a38346ccba2b9814174e46b67cd0": { "Name": "react_blog_admin", "EndpointID": "9a58ff20dc57d4d1fa6af83482051a68e80e22a5e37cf8e0cb3570b78102f107", "MacAddress": "02:42:ac:12:00:03", "IPv4Address": "" IPv6Address 172.18.0.3/16", ":" "}, "db0050257a8e8a0fa430ea04b009ae819dbf04ef001cf1027ec2b5565403b48e" : {" Name ": "react_blog_blog", "EndpointID": "664794ed292871bc7fd8e1c4eaa56f682a6be5d653209f84158f3334a4f30660", "MacAddress": "02:42: ac: 12:00:02", "IPv4Address" : "172.18.0.2/16", "IPv6Address" : ""}}," Options ": {}," Labels ": { "com.docker.compose.network": "react_blog", "com.docker.compose.project": "docker", "com.docker.compose.version": "1.25.1"}}]Copy the code

4.3 Calling An Interface

There is a problem, now we call interface in the code form is http://localhost:9002/api/xxx, call interface in react_blog_blog container localhost is itself, The interface in react_blog_service is not called.

In view of the admin

In the code, we call the interface like this

const HOST = process.env.NODE_ENV === 'development' ? 'http://localhost:9002' : ' '

const API = {
  getArticleList: HOST + '/api/getArticleList'.getArticle: HOST + '/api/getArticle'.addArticle: HOST + '/api/addArticle'.delArticle: HOST + '/api/delArticle'.getTagList: HOST + '/api/getTagList'.addTag: HOST + '/api/addTag'.delTag: HOST + '/api/delTag'.register: HOST + '/api/register'.login: HOST + '/api/login',}export default API
Copy the code

You’ll find interface 404, which we proxy through nginx

admin/docker/nginx.conf

server {
  listen 80;
  sendfile on;
  sendfile_max_chunk 1M;
  tcp_nopush on;
  gzip_static on;

  location /api {
    proxy_pass http://react_blog_service:9002;
  }

  location / {
    root /www;
    indexindex.html; }}Copy the code

For requests that start with/API, we forward them to the 9002 port of the react_blog_service container and drag nginx.conf to the server. Since we are mounting this file inside the container, we just need to restart the container

$ docker restart react_blog_admin
Copy the code

React_blog.sql (); / / react_blog.sql (); / / react_blog.sql (); / / react_blog.sql ()

In view of the blog

I thought through the environment variable (Next to be stored in the variable at Runtime Runtime Configuration) request to transfer the HOST (react_blog_service | | localhost). React_blog_service was not supported by the front-end interface (getServerSideProps was), so nginx was used to proxy the request. And we definitely needed to use the domain name to access the site, so we still needed nginx. So let’s add an Nginx container for the foreground page.

1. Create environment variables

blog/docker/Dockerfile

# node mirror
The # apline version of Node will be much smaller
FROM node:12-alpine

Create a directory in the container
RUN mkdir -p /usr/src/app

The following commands will be executed in the current directory
WORKDIR /usr/src/app

# copy package. Json
COPY package.json /usr/src/app

# install dependencies
RUN npm i --production --registry=https://registry.npm.taobao.org

# copy all other files to container (except directories and files in.dockerignore)
COPY . /usr/src/app

# Build phase capture
ENV HOST react_blog_service ## Add an environment variable, available during the build phase, that must precede the NPM run build line

# build
RUN npm run build

Expose port 9000
EXPOSE 9000

Dokcerfile can only have one CMD command per file. If multiple, only the last one will be executed
CMD [ "npm"."start" ]
Copy the code

In the code, the setup run is the variable blog/ next-config.js

const withCSS = require('@zeit/next-css')
const withLess = require('@zeit/next-less')
module.exports = () = >withLess({ ... withCSS(),// Change to nginx proxy
    publicRuntimeConfig: {
      HOST: process.env.HOST || 'localhost'.// If it is a docker build, run process.env.host, otherwise run localhsot}})Copy the code

In the blog/config/API

import getConfig from 'next/config'
const { publicRuntimeConfig } = getConfig()

const SSRHOST = `http://${publicRuntimeConfig.HOST}: 9002 `
const HOST = `http://localhost:9002`

export const SSRAPI = {
  getArticleList: SSRHOST + '/api/getArticleList'.getArticle: SSRHOST + '/api/getArticle',}export const API = {
  getArticleList: HOST + '/api/getArticleList'.getArticle: HOST + '/api/getArticle',}Copy the code

There’s a little bit of trouble here, I don’t know if I’m getting this right, but I’ve tried a lot of things and only this local and Docker deployment works.

  • If it is run locally (without docker), the server obtains the data (getServerSideProps) and the page obtains the data directly using the service interface address (localhost:9002)

  • If docker is running, the server needs to directly bring the address of the service interface container (getServerSideProps), not through the nginx proxy. If the page gets the data, the function is through the nginx proxy

2. Nginx proxy

Modify blog/docker/ docker-comemage. yml to add an nginx container

version: '3'
services:
  blog:
    build:
      context: ../
      dockerfile: ./docker/Dockerfile
    image: react_blog:blog
    # ports:
    # - 9000:9000
    networks:
      - react_blog
    container_name: react_blog_blog
  nginx:
    build:
      context: ../
      dockerfile: ./docker/Dockerfile-nginx
    image: react_blog:nginx
    ports:
      - 9000: 80
    volumes:
      - ./nginx.conf:/etc/nginx/conf.d/nginx.conf
    networks:
      - react_blog
    container_name: react_blog_nginx
networks:
  react_blog:
Copy the code

blog/docker/Dockerfile-nginx

FROM nginx

# delete default configuration of Nginx
RUN rm /etc/nginx/conf.d/default.conf

EXPOSE 80
Copy the code

blog/docker/nginx.conf

server {
  listen 80;
  sendfile on;
  sendfile_max_chunk 1M;
  tcp_nopush on;
  gzip_static on;

  location /api {
    proxy_pass http://react_blog_service:9002;
  }

  location / {
    proxy_passhttp://react_blog_blog:9000; }}Copy the code

3. Build containers

Docker-compose up -d –build will re-download NPM node_modules. Docker-compose up -d –build will re-download NPM node_modules.

Execute in the blog directory

$ docker build -f docker/Dockerfile . -t react_blog:blog
Copy the code

Execute under blog/docker

$ docker-compose up -d
Copy the code

If successful, try the interface again to get the data.

5. Connect to the host Mysql

I encountered a problem with Mysql on my server (host). Lost connection to mysql server at ‘reading initial communication packet’ 2013 lost connection to mysql server at ‘reading initial communication packet’ The systemctl start mysqld.service command is used to start the Mysql service. Because other projects were deployed separately before, without using Docker, and the data were all on the host Mysql, I still prefer unified management and adaptive host Mysql. Let’s see how to implement it.

There are two ways to do this

Mode 1: network_mode: host

Modify the service/docker docker – compose. Yml

version: '3'
services:
  service:
    build:
      context: ../
      dockerfile: ./docker/Dockerfile
    image: react_blog:service
    ports:
      - 9002: 9002
    restart: on-failure
    # depends_on:
    # - db
    environment:
      # MYSQL_HOST: react_blog_mysql
      MYSQL_USER: root
      MYSQL_PASSWORD: 8023
    volumes:
      - ../article:/usr/src/app/article
    network_mode: host
    # networks:
    # - react_blog
    container_name: react_blog_service
  # db:
  # image: mysql
  # ports:
  # - 33061:3306
  # restart: on-failure
  # command: --default-authentication-plugin=mysql_native_password
  # environment:
  # MYSQL_ROOT_PASSWORD: 8023
  # MYSQL_PASSWORD: 8023
  # MYSQL_DATABASE: react_blog
  # networks:
  # - react_blog
  # container_name: react_blog_mysql
networks:
  react_blog:
Copy the code

Service /docker to execute the command

$ docker-compose up -d

$ docker ps -aCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES AF9e525e7d14 react_blog:service "docker-entryPoint.s..." 28 seconds ago Up 26 seconds react_blog_serviceCopy the code

Docker inspect react_blog_service does not assign IP. This is equivalent to a Node application connecting itself to the host Mysql. But for page interface requests, because react_blog_service is no longer in docker_react_blog, it is accessed using the host IP address.

nginx.conf

server {
  listen 80;
  sendfile on;
  sendfile_max_chunk 1M;
  tcp_nopush on;
  gzip_static on;

  location /api {
    # proxy_pass http://react_blog_service:9002;
    proxy_pass http://xxx.xx.xxx.x:9002; # xxx.xx.xxx.x is the IP address of the host
  }

  location / {
    proxy_passhttp://react_blog_blog:9000; }}Copy the code

The same is true of the server-side render interface

# node mirror
The # apline version of Node will be much smaller
FROM node:12-alpine

Create a directory in the container
RUN mkdir -p /usr/src/app

The following commands will be executed in the current directory
WORKDIR /usr/src/app

# copy package. Json
COPY package.json /usr/src/app

# install dependencies
RUN npm i --production --registry=https://registry.npm.taobao.org

# copy all other files to container (except directories and files in.dockerignore)
COPY . /usr/src/app

# build phase, xxx.xx.xxx.x is the host (server)IP
ENV HOST xxx.xx.xxx.x

# build
RUN npm run build

Expose port 9000
EXPOSE 9000

Dokcerfile can only have one CMD command per file. If multiple, only the last one will be executed
CMD [ "npm"."start" ]
Copy the code

This way is not very troublesome, but also expose the server IP address, so I choose the second way

Method 2:

Modify the service/docker docker – compose. Yml

version: '3'
services:
  service:
    build:
      context: ../
      dockerfile: ./docker/Dockerfile
    image: react_blog:service
    ports:
      - 9002: 9002
    restart: on-failure
    # depends_on:
    # - db
    environment:
      MYSQL_HOST: 172.17. 01.
      MYSQL_USER: root
      MYSQL_PASSWORD: 8023
    volumes:
      - ../article:/usr/src/app/article
    networks:
      - react_blog
    container_name: react_blog_service
  # db:
  # image: mysql
  # ports:
  # - 33061:3306
  # restart: on-failure
  # command: --default-authentication-plugin=mysql_native_password
  # environment:
  # MYSQL_ROOT_PASSWORD: 8023
  # MYSQL_PASSWORD: 8023
  # MYSQL_DATABASE: react_blog
  # networks:
  # - react_blog
  # container_name: react_blog_mysql
networks:
  react_blog:
Copy the code

MYSQL_HOST is 172.17.0.1. The container can connect to the host using this IP address, so it connects to the host’s Mysql database.

6. A docker – compoer. Yml

Docker-composing. Yml: docker-composing. Yml: docker-composing. Yml: docker-composing

Create docker/ docker-comemage. yml in the project root directory to create a docker directory, in order to create a network and a single project run is created consistent

version: '3'
services:
  blog:
    build:
      context: ../blog
      dockerfile: ./docker/Dockerfile
    image: react_blog:blog
    networks:
      - react_blog
    container_name: react_blog_blog
  nginx:
    build:
      context: ../blog
      dockerfile: ./docker/Dockerfile-nginx
    image: react_blog:nginx
    ports:
      - 9000: 80
    volumes:
      - ../blog/docker/nginx.conf:/etc/nginx/conf.d/nginx.conf
    networks:
      - react_blog
    container_name: react_blog_nginx
  admin:
    build:
      context: ../admin
      dockerfile: ./docker/Dockerfile
    image: react_blog:admin
    ports:
      - 9001: 80
    volumes:
      - ../admin/build:/www
      - ../admin/docker/nginx.conf:/etc/nginx/conf.d/nginx.conf
    networks:
      - react_blog
    container_name: react_blog_admin
  service:
    build:
      context: ../service
      dockerfile: ./docker/Dockerfile
    image: react_blog:service
    ports:
      - 9002: 9002
    restart: on-failure
    environment:
      MYSQL_HOST: 172.17. 01.
      MYSQL_USER: root
      MYSQL_PASSWORD: 8023
    volumes:
      - ../service/article:/usr/src/app/article
    networks:
      - react_blog
    container_name: react_blog_service
networks:
  react_blog:
Copy the code

Stop and delete all containers created previously

$ docker stop $(docker ps -aq)
$ docker rm $(docker ps -aq)
Copy the code

Go to the /docker directory and execute,

$ docker-compose up -d
Building nginx
Step 1/3 : FROM nginx
 ---> ae2feff98a0c
Step 2/3 : RUN rm /etc/nginx/conf.d/default.conf
 ---> Running in bb163c42c6b5
Removing intermediate container bb163c42c6b5
 ---> 282cb303dddf
Step 3/3 : EXPOSE 80
 ---> Running in 9b77ebd39952
Removing intermediate container 9b77ebd39952
 ---> fbb18dda70af
Successfully built fbb18dda70af
Successfully tagged react_blog:nginx
WARNING: Image for service nginx was built because it did not already exist. To rebuild this image you must use `docker-compose build` or `docker-compose up --build`.
Building admin
Step 1/3 : FROM nginx
 ---> ae2feff98a0c
Step 2/3 : RUN rm /etc/nginx/conf.d/default.conf
 ---> Using cache
 ---> 282cb303dddf
Step 3/3 : EXPOSE 80
 ---> Using cache
 ---> fbb18dda70af
Successfully built fbb18dda70af
Successfully tagged react_blog:admin
WARNING: Image for service admin was built because it did not already exist. To rebuild this image you must use `docker-compose build` or `docker-compose up --build`.
Creating react_blog_admin   ... done
Creating react_blog_service ... done
Creating react_blog_blog    ... done
Creating react_blog_nginx   ... done

$ docker ps -a
CONTAINER ID   IMAGE      			COMMAND    CREATED          STATUS         PORTS                    NAMES
1fbb15abdd30   react_blog:service   "docker"   13 seconds ago   Up 6 seconds   0.0.0.0:9002->9002/tcp   react_blog_service
fbee53e25c3a   react_blog:admin     "/docker"  13 seconds ago   Up 6 seconds   0.0.0.0:9001->80/tcp     react_blog_admin
70cb25f87d14   react_blog:blog      "docker"   13 seconds ago   Up 6 seconds   9000/tcp                 react_blog_blog
aa9fbf2afea4   react_blog:nginx     "/docker"  13 seconds ago   Up 6 seconds   0.0.0.0:9000->80/tcp     react_blog_nginx
Copy the code

Running successfully ~

7. The domain name

I am now using the host’s nginx to proxy the domain name to IP:9000, and then to the react_blog_nginx container, trying to proxy directly in react_blog_nginx, but failed. React_blog_nginx is accessed through port mapping, host IP:9000. If you configure the domain name inside react_blog_nginx, it will always feel impossible to access it.

conclusion

Before finally finished writing, writing has been learning to try for a long time, thought is very sure, results in the process of writing and a pile of problems, a problem will probably card day for a long time, all kinds of baidu, Google, using tubing, finally solved all the problems, of course, these problems can only meet the demand of the deployment I now, there were a lot of knowledge, No, but that’s okay, I just want to successfully deploy the front-end project.

The above are all the notes of docker deployment front-end project, the content is rather wordy, I hope to help the later students to make less mistakes, because some of them are their own understanding, there may be mistakes, please correct, learn from each other, over.