天天看點

小白伺服器程式設計指北(2)——用Docker編配你的伺服器環境安裝Docker安裝Docker-compose編寫Docker file編寫Docker-compose.yml将鏡像上傳至阿裡雲鏡像倉庫可能的問題

安裝Docker

首先要安裝Docker。Docker底層使用的是Linux的容器技術。

是以,為了能夠使用Docker,我們需要一台安裝了相容版本的Linux核心和二進制檔案的最小化功能主控端。

筆者這裡使用了CentOS 7作業系統。

Step1. Update Docker Package Database

更新yum的repo:

sudo yum check-update
           

Step 2: Install the Dependencies

接下來安裝Docker的依賴庫:

sudo yum install -y yum-utils device-mapper-persistent-data lvm2
           

The yum-utils switch adds the yum-config-manager. Docker uses a device mapper storage driver, and the device-mapper-persistent-data and lvm2 packages are required for it to run correctly.

yum-utils會安裝yum-config-manager,用于我們下一步配置Docker repo。

Docker需要使用裝置存儲映射驅動(device mapper storage driver),是以,為了Docker能夠正确的運作,我們需要安裝device-mapper-persistent-data 和 lvm2 packages。

Step 3: Add the Docker Repository to CentOS

sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
           

這個yum repo會讓我們安裝最新版本的Docker。

Step 4: Install Docker On CentOS Using Yum

sudo yum install docker
           

Step: 5 Manage Docker Service

雖然我們安裝了Docker,但是Docker并沒有啟動。Docker是作為一種服務來運作的:

sudo systemctl start docker
           
sudo systemctl enable docker
           

OK,我們就完成了Docker的安裝,并啟動了Docker服務。

安裝Docker-compose

接下來我們來安裝Docker-compose。

Compose is a tool for defining and running multi-container Docker applications. With Compose, you use a YAML file to configure your application’s services. Then, with a single command, you create and start all the services from your configuration.
Compose是定義和運作多容器Docker應用程式的工具,使用Compose,您可以使用YAML檔案來配置應用程式的服務,然後,使用單個指令建立并啟動配置中的所有服務

在Linux系統中,安裝Docker-compose由兩種方式:

方法一:

sudo curl -L "https://github.com/docker/compose/releases/download/1.25.1/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
           
To install a different version of Compose, substitute 1.25.1 with the version of Compose you want to use.

為Docker-compose添加可執行權限:

sudo chmod +x /usr/local/bin/docker-compose
           

方法二:

利用pip:

#安裝pip
$yum -y install epel-release
$yum -y install python-pip
#确認版本
$pip --version
#更新pip
$pip install --upgrade pip
#安裝docker-compose
$pip install docker-compose 
#檢視版本
$docker-compose version
           

注意在安裝Docker-compose的時候,我們需要安裝一下系統的編譯支援:

yum install gcc
yum install gcc-c++
yum install python-devel -y
           

編寫Docker file

Docker file用于Docker編譯我們的工程。在編寫Docker的時候,要注意的是,Docker file的路徑,全部是基于Docker file所在目錄的。

下圖是docker file的目錄配置:

小白伺服器程式設計指北(2)——用Docker編配你的伺服器環境安裝Docker安裝Docker-compose編寫Docker file編寫Docker-compose.yml将鏡像上傳至阿裡雲鏡像倉庫可能的問題

最外層的Docker file是用來配置我們的Web 工程,基于python:3.6的鏡像:

FROM python:3.6

RUN mkdir /ai_mei_jia

WORKDIR /ai_mei_jia

ADD . /ai_mei_jia

RUN pip3 install -r requirements.txt

EXPOSE 8080

ENV DJANGO_SETTINGS_MODULE=ai_mei_jia.settings.pro

# 賦予start.sh可執行權限
RUN chmod u+x start.sh

#容器啟動後要執行的指令
CMD bash ./start.sh
           

requirements.txt中配置需要Django工程依賴的python庫:

uwsgi==2.0.18
psycopg2-binary==2.8.4
Django==3.0.1
djangorestframework==3.11.0
pillow==6.2.1
django-rest-auth==0.9.5
djangorestframework-jwt==1.11.0
django-redis==4.11.0
aliyun_python_sdk_core
           

start.sh用來在容器啟動時,執行必要的shell指令:

python manage.py collectstatic --noinput &&
python manage.py makemigrations &&
python manage.py migrate &&
uwsgi --ini config/uwsgi.ini
           

在Nginx目錄中,我們配置了Nginx需要的Docker file,主要是将我們的Nginx配置檔案,拷貝到Nginx容器中:

FROM nginx

# 對外暴露端口
EXPOSE 80 8000

COPY ./config/ai_mei_jia_nginx.conf /etc/nginx/conf.d/
           

在Redis目錄中,配置了Redis需要的Docker file:

FROM redis:5.0
COPY redis.conf /usr/local/etc/redis/redis.conf
CMD [ "redis-server", "/usr/local/etc/redis/redis.conf" ]
           

編寫Docker-compose.yml

我們編寫好Docker file之後,就可以在Docker-compose.yml檔案中,将這些鏡像組織起來了:

version: '3'

services:
  db:
    image: postgres:12
    restart: always
    volumes:
      - ./postgredata:/var/lib/postgresql/data
    environment:
      - POSTGRES_USER=eshi
      - POSTGRES_DB=ai_mei_jia_db
      - POSTGRES_PASSWORD=sw123!

  redis:
      image: redis:5
      restart: always
      command: redis-server
      ports:
        - "6379:6379"
      volumes:
        - ./redisdata:/data

  web:
    build: .
    restart: always
    ports:
      - "8080:8080"
    volumes:
      - .:/ai_mei_jia
      - /tmp/:/tmp/
    depends_on:
      - db
      - redis

  nginx:
    build: ./nginx
    ports:
      - "80:80"
    volumes:
      - ./nginx/config:/etc/nginx/conf.d
      - /tmp/:/tmp/
      - ./media:/usr/share/nginx/html/media
      - ./static:/usr/share/nginx/html/static
    restart: always
    depends_on:
      - web
           

對于鏡像的建構,docker-compose有兩種方式:

build

image

。build的方式後面跟的内容是建構鏡像所需的Docker file的路徑,如:

...
nginx:
    build: ./nginx
    ...
           

而image的方式則是之間使用倉庫中已有的鏡像,後面跟的内容是鏡像的倉庫路徑,這種情況是不需要本地建構鏡像的,而是直接将倉庫中的鏡像pull到本地。如:

...
redis:
      image: redis:5
      ...
           

我們用

depends_on

設定了容器之間的依賴關系,在容器啟動時,會根據

depends_on

的順序來依次啟動容器。需要注意的是,這裡的容器啟動,并不能夠確定容器内的資料庫能夠完全啟動,是以可能會發生web連接配接不上db的問題。

我們用

volumes

設定了主控端與容器之間的

卷映射

,這樣做的好處是,我們能夠直接在主控端修改檔案内容,如伺服器的源代碼,或一些配置檔案,而不必重新建構鏡像。

注意,這裡映射的主控端的路徑,是相對于docker-compose.yml檔案的

現在,我們在docker-compse檔案所在的目錄,執行:

docker-compose build
           

來建構我們的鏡像吧!

Docker Networking

在docker-compose檔案中,我們一共建構了4個鏡像:

db, redis, web, nginx。(當然最後的鏡像名稱并不是這4個名稱,這4個名稱主要是用來在docker-compose中容器間的通信)。

我們用depend-on設定了容器之間的依賴關系。

那麼,這些容器間是如何通信的呢?

在Docker中,容器之間的通信通過網絡建立,這被稱為Docker Networking,也是Docker 1.9釋出版本中的新功能。

當我們用

docker-compose up

啟動一組容器後,如果沒有指定名稱,則docker會預設建立一個

Docker Networking網絡

,容器間通過這個網絡來通信。可以了解為Docker建立了一個虛拟的區域網路,docker-compose中的每個容器,都是這個區域網路内的一個主機,由自己的ip,容器通過這個虛拟區域網路來通信。

我們可以用下面指令來檢視目前Docker中的networking 清單:

NETWORK ID          NAME                      DRIVER              SCOPE
63ba93338b3a        ai_mei_jia_copy_default   bridge              local
60090ef6f4ce        ai_mei_jia_default        bridge              local
a5395928d7f8        bridge                    bridge              local
abf7689564f0        host                      host                local
bb9594ed8810        none                      null                local
           

network分為兩種,一種是bringe,一種是overlay。通過overlay模式,我們可以使不同的主控端間的容器進行通信。在這裡,我們隻是在同一個主控端上設定了一個bridge網絡。

我們可以通過

inspect

指令來檢視一個網絡中的情況:

docker network inspect ai_mei_jia_copy_default
           
[
    {
        "Name": "ai_mei_jia_copy_default",
        "Id": "63ba93338b3a86d23723209077b2afac1b4afa94c673e63113153f8031b30e71",
        "Created": "2020-01-21T14:19:06.957571863+08:00",
        "Scope": "local",
        "Driver": "bridge",
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": null,
            "Config": [
                {
                    "Subnet": "172.19.0.0/16",
                    "Gateway": "172.19.0.1"
                }
            ]
        },
        "Internal": false,
        "Attachable": true,
        "Containers": {
            "5354921a5c3caa027f88292913f8924a4d627e9a282a115444a73e468d09f357": {
                "Name": "ai_mei_jia_copy_nginx_1",
                "EndpointID": "0a4f9714d4f7a3da0847dc2d50fad39b26e8a211b9b9c01091cfdfc2ae20bad5",
                "MacAddress": "02:42:ac:13:00:05",
                "IPv4Address": "172.19.0.5/16",
                "IPv6Address": ""
            },
            "6ac4a5db1508ac65f9dbe0fea5c698726703a9f1ba23b83a04d9495c298ba75f": {
                "Name": "ai_mei_jia_copy_redis_1",
                "EndpointID": "df03597ad99434dd735c2bf78cf01777a16ae6c6feaf742c67ca6624ee0ae870",
                "MacAddress": "02:42:ac:13:00:02",
                "IPv4Address": "172.19.0.2/16",
                "IPv6Address": ""
            },
            "cc56e63dbb76d468a1338b1c621807604fc75ac4e3797067bf544500c2b4e41f": {
                "Name": "ai_mei_jia_copy_db_1",
                "EndpointID": "67e6f297948f62783737807b577ede01f52fa61b603508f8190b9fd21bd70baf",
                "MacAddress": "02:42:ac:13:00:03",
                "IPv4Address": "172.19.0.3/16",
                "IPv6Address": ""
            },
            "e852a487cf3ab64fd373ec1f6158fe1f58ac02bfe1201c1ca78afa1f4f8fa50d": {
                "Name": "ai_mei_jia_copy_web_1",
                "EndpointID": "8e4b4ae0034e207b46bf6b248ab6219bd2622ce5cdf4e6ab94aa345972ab1308",
                "MacAddress": "02:42:ac:13:00:04",
                "IPv4Address": "172.19.0.4/16",
                "IPv6Address": ""
            }
        },
        "Options": {},
        "Labels": {
            "com.docker.compose.network": "default",
            "com.docker.compose.project": "ai_mei_jia_copy",
            "com.docker.compose.version": "1.25.1"
        }
    }
]
           

當容器通過docker-compse連接配接起來後,容器之前的位址就可以用docker-compose配置檔案中的名稱來代替了,如,在Django的配置檔案中,我們可以設定redis和Postgresql的位址為:

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql',
        ...,
        'HOST': 'db', # set in docker-compose.yml
        'PORT': 5432 # default postgres port
    }
}
           
CACHES = {
    'default': {
        'BACKEND': 'django_redis.cache.RedisCache',
        'LOCATION': 'redis://redis:6379/1',  # redis(容器)
        'OPTIONS': {
            'CLIENT_CLASS': 'django_redis.client.DefaultClient',
        }
    }
}
           

将鏡像上傳至阿裡雲鏡像倉庫

當本地鏡像建構完畢後,我們就需要将鏡像上傳當倉庫,用來在其他的機器上使用我們的鏡像。Docker由官方的鏡像網站Docker Hub。由于其是國外的網站,上傳下載下傳都比較慢,是以我們選用了國内的阿裡雲Docker鏡像網站。

在阿裡雲的控制台搜尋容器鏡像服務:

小白伺服器程式設計指北(2)——用Docker編配你的伺服器環境安裝Docker安裝Docker-compose編寫Docker file編寫Docker-compose.yml将鏡像上傳至阿裡雲鏡像倉庫可能的問題

開通服務後,建立一個鏡像倉庫:

小白伺服器程式設計指北(2)——用Docker編配你的伺服器環境安裝Docker安裝Docker-compose編寫Docker file編寫Docker-compose.yml将鏡像上傳至阿裡雲鏡像倉庫可能的問題

當鏡像建立完畢後,你就會得到一個鏡像倉庫的位址,copy下。

回到主控端,登入我們剛才的阿裡雲容器倉庫:

sudo docker login --username=erenshi registry.cn-hangzhou.aliyuncs.com
           

為我們本地的鏡像打tag:

docker tag ai_mei_jia_web:v3 registry.cn-hangzhou.aliyuncs.com/ai_mei_jia_test/ai_mei_jia_test1/ai_mei_jia_web:v3 
           

push 到倉庫:

docker push registry.cn-hangzhou.aliyuncs.com/ai_mei_jia_test/ai_mei_jia_test1/ai_mei_jia_web:v3 
           

等待一會兒,阿裡雲的容器倉庫裡就有我們的鏡像啦。不過很奇怪的是,在阿裡雲的控制台中,并看不到我們上傳的鏡像,這也許是阿裡雲的一個bug:

小白伺服器程式設計指北(2)——用Docker編配你的伺服器環境安裝Docker安裝Docker-compose編寫Docker file編寫Docker-compose.yml将鏡像上傳至阿裡雲鏡像倉庫可能的問題

現在,就來修改docker-compose檔案中的内容,将本地的build指令,改為使用倉庫中的鏡像:

web:
    build: .
           

修改為:

web:
    image: registry.cn-hangzhou.aliyuncs.com/ai_mei_jia_test/ai_mei_jia_test1/ai_mei_jia_web:v3
           

nginx:
    build: ./nginx
           

修改為:

nginx:
    image: registry.cn-hangzhou.aliyuncs.com/ai_mei_jia_test/ai_mei_jia_test1/ai_mei_jia_copy_nginx:v1 
           

OK! 讓我們到新的主控端上,将docker-compose.yml拷貝過來,并将我們的web源碼拷貝到對應的目錄(因為我們在docker-compose中設定了卷映射,源碼會完全映射到web容器的work 目錄,是以如果不将源碼複制過來,web容器會報找不到檔案錯誤)。

編配鏡像:

docker-compose build
           

編配鏡像,因為我們此時的鏡像都采用了image模式編配,是以build并不會執行任何事情,而是會等到真正運作的時候,docker才會到倉庫中将需要的鏡像pull下來。

運作鏡像:

docker-compose up -d
           

這時docker會到倉庫将鏡像pull到本地,并運作容器。

輸入

檢視容器的運作狀态,如果正常的話,應該都是

up

狀态:

小白伺服器程式設計指北(2)——用Docker編配你的伺服器環境安裝Docker安裝Docker-compose編寫Docker file編寫Docker-compose.yml将鏡像上傳至阿裡雲鏡像倉庫可能的問題

如果發現有的容器一直是Restaring狀态,有可能是容器内部在啟動時發生了錯誤。這時候可以執行:

docker logs CONTAINER_ID
           

來檢視容器輸出的log。

如果還有其他的問題,我們還可以通過指令:

docker exec -it CONTAINER_ID /bin/bash
           

登入到容器内部。

可能的問題

在docker環境中,可能的問題多是由于Linux防火牆或SELinux引起的。

對于防火牆,我們可以通過指令

firewall-cmd -state
           

來檢視防火牆的運作狀态,

并通過:

sudo firewall-cmd --zone=public --permanent --add-port=80/tcp
sudo firewall-cmd --reload
           

來允許http協定通過80端口來通路我們的伺服器。

對于SELinux,我們可以通過指令

sestatus
           

來檢視SELinux的運作狀态,并通過修改SELinux的配置檔案:

vim /etc/selinux/config
           

來設定是否啟動SELinux。設定完畢後,需要通過指令

shutdown -r now
           

重新開機機器來使設定生效。

容器無法解析域名

當我們在容器内部用域名通路外網時,有時可能出現

name unresolved

的問題。如筆者在使用阿裡雲SDK發送短信時,SDK提示

name unresolved

錯誤。這是因為容器的DNS沒有設定正确引起的,這多發生在用虛拟機運作Linux的情況下。

這時候,我們需要修改docker的deamon.json檔案:

vim /etc/docker/daemon.json
           

添加主控端的DNS位址:

{
"dns": ["192.168.57.5"],
}
           

然後重新開機docker服務:

systemctl restart docker
           

無法通路伺服器

但我們在阿裡雲伺服器正确啟動伺服器容器後,通過浏覽器通路伺服器網址,卻發現無法通路伺服器。這是為啥呢?

原來是因為阿裡雲伺服器的安全組預設沒有開放80端口的緣故。這時候我們需要到阿裡雲ECS控制台,選擇我們的伺服器執行個體,進入後,選擇

本執行個體安全

組:

小白伺服器程式設計指北(2)——用Docker編配你的伺服器環境安裝Docker安裝Docker-compose編寫Docker file編寫Docker-compose.yml将鏡像上傳至阿裡雲鏡像倉庫可能的問題

内網入方向全部規則

下面,添加80端口(即Nginx所監聽的端口号):

小白伺服器程式設計指北(2)——用Docker編配你的伺服器環境安裝Docker安裝Docker-compose編寫Docker file編寫Docker-compose.yml将鏡像上傳至阿裡雲鏡像倉庫可能的問題

再次嘗試通路網址,這次伺服器應該就通了!