preface
A year ago, I renewed the server, the original server is the combination of Nginx + Jenkins + Docker, because of the strong interest in the updated tools, so I thought of reinstalling the new toy, began to use Traefik + DroneCi + Docker to replace the original combination of the road.
After the initial completion of service construction, I want to write this article to leave a memory of the pit I have encountered, and also hope to help other people who have just entered the pit.
Traefik
Traefik is an open source reverse proxy and load balancing tool. Many people compare Traefik to Nginx. In my case, traefik doesn’t support static sites well, so I still use it with Nginx. But that doesn’t detract from the fact that it’s an excellent reverse proxy tool.
Configuration and Startup
The Traefik configuration includes static configuration and dynamic configuration. The static configuration takes effect only after Traefik is restarted. The dynamic configuration is regarded as the proxy configuration and does not need to be restarted after modification. Cli and configuration files are supported for both dynamic and static configurations. However, configuration files and Cli parameters cannot be superimposed.
-
Static configuration
For example, starting the Traefik service in docker-compose, we can take a look at the cli and configuration file:
services: traefik: restart: always image: traefik:latest ports: - "80:80" - "443:443" # command: # - "--providers.docker=true" # - "--providers.docker.exposedbydefault=false" volumes: - /var/run/docker.sock:/var/run/docker.sock - ./acme.json:/acme.json - ./traefik/:/etc/traefik If there is a configuration file, command is invalid who: image: containous/whoami labels: - "traefik.enable=true" - "traefik.http.routers.whoa.rule=Host(`who.nefelibata.art)" Copy the code
We configured the Traefik container and mapped the./traefik directory containing traefik.toml and dynamic.toml files to /etc/traefik of the container. Traefik reads traefik. Toml in /etc/traefik on startup, and if you want to use cli, you pass in configuration parameters through command.
-
Dynamic configuration
-
Configuration file format
First we need the following configuration in Traefik. Toml:
[providers] ## ... [providers.file] filename = "/etc/traefik/dynamic_conf.toml" watch = true Copy the code
Configure routers and services in Dynamic_conf. toml
[http.routers] [http.routers.https-egg] rule = "Host(`egg.nefelibata.art`)" service = "egg-service" [http.phones.http-egg] rule = "Host(' egg.nefelibata.art ')" service = "egg-service" [http.services] [[http.services.egg-service.loadBalancer.servers]] url = "http://egg_server:9000"Copy the code
For example, HTTP. Phones.https -egg is used to enable TLS, but HTTP
Note that we defined two routers for egg.nefelibata.art. This is because HTTP access is no longer supported if TLS is set to true. If you want to support both HTTP and HTTPS access, you need to define a route with a different name
-
Docker labels form
Providers in traefik. Toml, if we don’t want to configure them all in a dynamic configuration file, we can write the following configuration:
[providers] [providers. Docker] # network = "traefik" exposedByDefault = false defaultRule = "Host(' {{normalize ' .Name }}.nefelibata.localhost`)" watch = true ## ... Other configurationCopy the code
For example, if you change the dynamic configuration to labels, it will look like this:
egg_server: build: ./egg_server expose: - "9000" networks: - default labels: - "traefik.enable=true" - "traefik.http.routes.egg_server.rule=Host(`egg.nefelibata.art`)" Copy the code
There is no need to configure services, just expose the port to the container.
Note that if the exposedByDefault option is turned off in configuration, the container service will be ignored by Traefik if traefik.enable=true is not defined in labels of other containers
-
Open the Dashboard
Traefik comes with a Dashboard. If you want to start Traefik and configure a domain name for it, you can configure it dynamically as described above, using a profile as an example:
## traefik. Toml ## [API] ### Other configuration... providers, ping .etc ## dynamic.toml [http.routers.api] rule = "Host(`traefik.nefelibata.art`)" service = "api@internal" middlewares = ["dashboard-auth"] [http.middlewares] [http.middlewares.dashboard-auth.basicAuth] users = [ "evont: $xxxxxxxxxxx" ]Copy the code
When you enable the API option in Traefik. Toml (or on the CLI — API =true), traefik has a special service called api@internal. Once configured, traefik usually performs authentication to prevent access. So I add a middlewares, use Traefik’s basicAuth middleware, use htpasswd to generate a user key, note that your name is Evont, your password is 123456, The result is evont:$APR1 $bL6G3wl2$HllalTsbNwJ/zhoBMhx541. When you log in to Dashboard, the password is still 123456 instead of the key string.
After the configuration is successful, restart the service and open the domain name of the service. The login page is displayed
Static Website support
Traefik doesn’t support static sites very well (at least I haven’t found a way to use it), so I can only use Nginx as a server for static sites, but ports 443 and 80 can’t be used for both reverse proxies, so Traefik can only forward requests to Nginx. We start a Nginx service, specify networks so that Nginx and Traefik are in the same network, and then assign the defined domain names to Nginx as labels
services:
nginx:
restart: always
image: nginx
networks:
- default
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf
- ./html:/usr/share/nginx/html/
labels:
- traefik.enable=true
- traefik.http.routers.w3.rule=Host(`www.nefelibata.art`) || Host(`mock.nefelibata.art`)
- traefik.http.routers.w3.tls=true
- traefik.http.routers.w3.tls.certresolver=le
- traefik.http.routers.w3-http.rule=Host(`www.nefelibata.art`) || Host(`mock.nefelibata.art`)
Copy the code
In the Nginx nginx.conf file, port 80 is still used and the directory of the static website is allocated according to server_name
http { server { listen 80; server_name www.nefelibata.art; root /usr/share/nginx/html/www1; location / { index index.html; } } server { listen 80; server_name mock.nefelibata.art; root /usr/share/nginx/html/www2; location / { index index.html; }}}Copy the code
HTTPS support (also HTTP support)
Certresolver = “le”; TLS = “HTTPS”;
If you want to automatically generate a certificate using Let’s Encrypt, Traefik provides a convenient solution. You can simply use the following configuration in static configuration:
## traefik. Toml ## [certificatesResolvers. Le. Acme] email = "[email protected]" # your email storage = "acme. Json" [certificatesResolvers.le.acme.httpChallenge] entryPoint = "http"Copy the code
In docker-compose, configure volumes to map acme.json to the docker-compose container
If you have your own certificate, you can also skip the above steps and configure it in dynamic.toml
[[tls.certificates]]
certFile = "/path/to/domain.cert"
keyFile = "/path/to/domain.key"
Copy the code
See Traefik’s TLS configuration for this section
Version of the pit
At the beginning of the configuration, most tutorials on the Internet were still based on V1, so the configuration was not successful. Later, IT was found that the configuration items were different between V1 and V2 versions, and even traefik Chinese documents were still based on V1 configuration, such as when defining routing rules
## v1 rule: [frontends] # # regulation front end into the rules/frontends. Frontend1 backend = "backend1" # specified back-end services [frontends. Frontend1. Routes] # # define routing [frontends. Frontend1. Routes. Route0] rule = "Host: test. The localhost" attention # #, Here writing also changed [backends] # # define back-end services [backends. Backend1] [backends. Backend1. The servers. Server0] url = "http://xx.xx.xx.xx:80" # # v2 [HTTP.router0] Rule = "Host(' test.localhost ')" ## Writing changed service = "my - service" [HTTP. Services] [[HTTP. Services. My - service. The loadBalancer. The servers]] url = "http://xx.xx.xx.xx:80"Copy the code
In addition, the above rules are defined in the [file] field in V1, and in V2, they are defined in the [providers. File] field in [providers] and become independent dynamic configuration files
In this regard, I recommend reading the official migration documentation and using the official documentation as a baseline (although it is also loosely written and not very friendly to those with poor English).
Drone CI
I used to use Jenkins, a CI tool of industry standard, but I always felt that it was a little cumbersome because of its rich functions. Drone has optimized the container environment of Docker and K8s, and is lightweight and flexible enough. If you don’t know how to choose between the two, you can read this article
OAuth
First of all, Drone only supports Git. Take Github as an example. To pull the code, you need to create an OAuth application in Github Developer Settings (you can use other Git repositories) and fill in the domain name of your Drone service. Note that the callback URL needs to be login
The installation
It is still based on the idea that all the services of the server are in Docker. A new Drone service is created in docker-comemage.yml. Many tutorials will configure drone-runner agent (agents), but it is not necessary. You can actually use drone-Server all by yourself
version: "3"
services:
drone-server:
image: drone/drone:latest
labels:
- traefik.http.routers.drone.rule=Host(`ci.nefelibata.art`)
- traefik.enable=true
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- /etc/docker/:/etc/docker
- ./drone:/var/lib/drone/ Note that this directory is used to place SQLite files, if using mysql or another database, as appropriate
restart: always
networks:
- default
environment:
- DRONE_OPEN=TRUE
- DRONE_ADMIN=xxx
- DRONE_USER_CREATE=username:xxx,admin:true
- DRONE_DATABASE_DATASOURCE=/var/lib/drone/drone.sqlite # point to the directory
- DRONE_DATABASE_DRIVER=sqlite3 # database engine
- DRONE_RPC_SECRET=${DRONE_RPC_SECRET}
- DRONE_RPC_PROTO=${DRONE_RPC_PROTO}
- DRONE_AGENTS_DISABLED=true
- DRONE_GITHUB_CLIENT_ID=${DRONE_GITHUB_CLIENT_ID}
- DRONE_GITHUB_CLIENT_SECRET=${DRONE_GITHUB_CLIENT_SECRET}
- DRONE_SERVER_HOST=${DRONE_SERVER_HOST}
- DRONE_SERVER_PROTO=${DRONE_SERVER_PROTO}
networks:
default:
external:
name: traefik
Copy the code
${XXX} ${XXX} ${XXX} ${XXX} ${XXX}
DRONE_GITHUB_CLIENT_ID=xxxxx Enter the client ID generated by OAuth
DRONE_GITHUB_CLIENT_SECRET=xxxxx # fill in the OAuth generated client secret
DRONE_RPC_SECRET=xxxxx This can be generated by openssl Rand-HEX 16
DRONE_SERVER_HOST=ci.nefelibata.art
DRONE_SERVER_PROTO=http
DRONE_RPC_PROTO=http
Copy the code
Drone registration is public by default, that is, all people who have access to your CI address can register and use your CI system. If you want to limit users, you can configure -drone_user_filter = Evont, XXX in the environment. Add users who are allowed to join (but not those who have previously registered, strange logic).
Start the service, access the service domain name, and jump to Github for login. If the login users are restricted in the previous step and the current Github account name is not allowed, the login fails when you return to the service.
If successful, you should see a list of your Github repository projects
Project settings
Trusted
Protected
Trusted
Volumes
The project build
Taking my project as an example, I hope that after submitting the code, Drone will help me build the mirror and then push it to the mirror warehouse.
Create a new project, after completing the basic code, create a following.drone.yml under the root directory of the project, use the Docker plug-in of drone, and specify the address and branch of the mirror warehouse.
Since you are using a private repository, you need to log in. At this time, we can fill in some variables in the Secrets column of CI interface for the construction process, so that we will not be exposed to.drone.yml. Add custom DOCKER_USERNAME and DOCKER_PASSWORD fields in the Secrets section as shown in the previous figure, then pass username and password through from_secret in.drone.yml, You don’t need to write it in a configuration file so that it can’t be seen by anyone else who has access to the code.
In addition, because the speed of pulling Docker images is very slow, you can specify the Docker acceleration source by setting the mirror.
---
kind: pipeline
type: docker
name: default
steps:
- name: egg-docker
image: plugins/docker
settings:
mirror: https://xxxx(own user ID).mirror.aliyuncs.com
username:
from_secret: DOCKER_USERNAME
password:
from_secret: DOCKER_PASSWORD
repo: registry.cn-hangzhou.aliyuncs.com/nefelibata/egg
registry: registry.cn-hangzhou.aliyuncs.com
auto_tag: true
trigger:
branch:
- master
event:
- push
Copy the code
Once confirmed, commit code to the project to trigger a build. If the version of Drone is larger than 1.4.0 and Agent is not enabled, you are likely to be stuck in Pending state like me. This is because Drone is in multi-machine mode by default. If it is a single server, you do not need to set a proxy server. Many configurations on the web teach DRONE_AGENTS_ENABLED=false, but DRONE_AGENTS_DISABLED=true should be used to enable single-machine mode.
Once set up and the build is triggered, you can see that the project has a build process, which pulls the project code into a temporary directory that will be destroyed when the build is complete.
Build complete, push to mirror warehouse, scatter flowers!
Docker
-
networks
The containers of different Docker Compose are different. Each Docker Compose has its own networks. The services mentioned above are separated in different docker-comemess. yml files. We need to make them in the same network. In this case, we can first establish a shared network by executing Docker network Create Treaefik. Then configure networks in each docker-comemage. yml to point to the newly created networks, and finally specify its networks in the container
services: nginx: #... image: nginx networks: - default #... networks: default: external: name: treaefik Copy the code
-
other
There are many docker related articles, but I have not encountered any problems in this aspect in the project for the time being, which needs to be recorded.